Problem Summary

For each positive integer \(n\), define

$$\operatorname{rad}(n)=\prod_{p\mid n} p,$$

the product of the distinct prime divisors of \(n\), with \(\operatorname{rad}(1)=1\). The problem orders all integers \(1 \le n \le 100{,}000\) by the pair \((\operatorname{rad}(n),n)\): smaller radical first, and if two numbers have the same radical, the smaller integer comes first. If \(E(k)\) denotes the \(k\)-th term of that ordered list, the goal is to determine \(E(10000)\).

The only real computational issue is that the order depends on all \(100{,}000\) radical values. The implementations avoid factoring each number separately. Instead, they compute every radical in one sieve-like pass and then perform a single sort using the exact order required by the statement.

Mathematical Approach

The structure of the problem is simple but very specific: compute \(\operatorname{rad}(n)\) for every \(n\), sort the resulting pairs, and read one position. The mathematical content lies in finding the right way to build the whole radical table efficiently.

The ordering that defines \(E(k)\)

Write

$$a \prec b \quad\Longleftrightarrow\quad \bigl(\operatorname{rad}(a),a\bigr) \lt \bigl(\operatorname{rad}(b),b\bigr)$$

in lexicographic order. Then \(E(1),E(2),\dots,E(100000)\) is just the list of integers \(1,2,\dots,100000\) sorted by \(\prec\). This formulation matters because ties are not arbitrary: if \(\operatorname{rad}(a)=\operatorname{rad}(b)\), then the smaller of \(a\) and \(b\) must appear first.

Radicals discard exponents

If

$$n=\prod_{i=1}^{r} p_i^{e_i},$$

then

$$\operatorname{rad}(n)=\prod_{i=1}^{r} p_i.$$

So the radical remembers which primes divide \(n\), but forgets how many times they occur. For example, \(2\), \(4\), \(8\), and \(16\) all have radical \(2\); likewise \(3\) and \(9\) both have radical \(3\). This is why powers of small primes move forward in the ordered list even when the numbers themselves are larger. It also gives the standard inequality

$$\operatorname{rad}(n)\le n,$$

with equality exactly when \(n\) is squarefree.

A sieve that multiplies each prime exactly once

The key observation is that the radical of \(m\) is obtained by multiplying together the distinct primes dividing \(m\). Therefore one may initialize an array \(R\) by \(R[n]=1\) for all \(n\), and then for each prime \(p \le N\) multiply \(p\) into every multiple of \(p\):

$$R[m]\leftarrow R[m]\cdot p \qquad \text{for } m=p,2p,3p,\dots.$$

Because the loop advances in steps of \(p\), each multiple receives the factor \(p\) exactly once, no matter whether \(p\) divides it once or many times. After all primes have been processed, every distinct prime divisor of \(m\) has contributed one factor, so

$$R[m]=\prod_{p\mid m} p=\operatorname{rad}(m).$$

The primality test used by the implementations is also sieve-based: a boolean array marks composites as soon as they appear as nontrivial multiples of a smaller prime. When the outer loop reaches an unmarked value, that value is prime and should be propagated through its multiples.

Why the sieve is correct

An easy invariant proves correctness. After finishing all primes up to some threshold \(q\), the current value \(R[n]\) equals the product of those prime divisors of \(n\) that are at most \(q\). Initially this is true with an empty product. When the next prime \(p\) is processed, exactly the multiples of \(p\) are updated, so each such number gains the missing factor \(p\), and all other numbers remain correct. By induction, after the final prime the full radical has been built for every \(n\).

Worked example: the range \(1\) through \(10\)

For the first ten integers, the radicals are

$$\operatorname{rad}(1)=1,\ \operatorname{rad}(2)=2,\ \operatorname{rad}(3)=3,\ \operatorname{rad}(4)=2,\ \operatorname{rad}(5)=5,$$

$$\operatorname{rad}(6)=6,\ \operatorname{rad}(7)=7,\ \operatorname{rad}(8)=2,\ \operatorname{rad}(9)=3,\ \operatorname{rad}(10)=10.$$

Sorting the pairs \((\operatorname{rad}(n),n)\) gives

$$ (1,1),\ (2,2),\ (2,4),\ (2,8),\ (3,3),\ (3,9),\ (5,5),\ (6,6),\ (7,7),\ (10,10). $$

Hence

$$E(4)=8.$$

This example shows both parts of the ordering: \(4\) and \(8\) move ahead because their radical is only \(2\), and ties among equal radicals are broken by the ordinary size of the integers.

From the sorted list to the requested term

Once all radicals are known, the rest of the problem is purely an ordering step. Build the sortable collection, order it lexicographically by \((\operatorname{rad}(n),n)\), and take the \(10000\)-th entry. There is no hidden recurrence or deeper search after the sieve; the whole solution is exactly this radical table plus one sort.

How the Code Works

The C++, Python, and Java implementations all follow the same three-stage algorithm.

Building the radical table

An array of length \(N+1\) is initialized with \(1\). A second boolean array is used to recognize primes during the sweep from \(2\) to \(N\). Whenever the current value is still unmarked, it is prime, so the implementation multiplies that prime into every multiple and marks the nontrivial multiples as composite for later iterations.

Sorting by the exact problem key

After the sieve, the implementation creates a collection representing all integers \(1\) through \(100{,}000\) together with their radicals, or equivalently sorts the integers directly by the key \((\operatorname{rad}(n),n)\). The secondary key is essential; without it, equal-radical groups would not match the required definition of \(E(k)\).

Reading the target position

The final answer is the element in position \(10000\) under 1-based counting, which is array index \(9999\) under 0-based storage. One of the implementations also checks the miniature example above to confirm that the ordering rule produces \(E(4)=8\) when the limit is \(10\).

Complexity Analysis

Let \(N=100{,}000\). The sieve phase visits each multiple of each prime once, so its running time is

$$\sum_{p\le N}\left\lfloor \frac{N}{p}\right\rfloor = O\!\left(N\sum_{p\le N}\frac{1}{p}\right)=O(N\log\log N).$$

Sorting \(N\) items by \((\operatorname{rad}(n),n)\) costs \(O(N\log N)\), which is the dominant term here. The memory usage is \(O(N)\): one array for radical values, one boolean array for the sieve state, and one sortable collection containing the integers or the corresponding pairs.

Footnotes and References

  1. Project Euler Problem 124: https://projecteuler.net/problem=124
  2. Radical of an integer: Wikipedia - Radical of an integer
  3. Sieve of Eratosthenes: Wikipedia - Sieve of Eratosthenes
  4. Square-free integer: Wikipedia - Square-free integer
  5. Lexicographic order: Wikipedia - Lexicographic order

Problemzusammenfassung

Für jede positive ganze Zahl \(n\) sei

$$\operatorname{rad}(n)=\prod_{p\mid n} p,$$

also das Produkt der verschiedenen Primteiler von \(n\), mit \(\operatorname{rad}(1)=1\). Das Problem ordnet alle Zahlen \(1 \le n \le 100{,}000\) nach dem Paar \((\operatorname{rad}(n),n)\): zuerst nach dem Radikal, bei Gleichstand nach der Zahl selbst. Bezeichnet \(E(k)\) das \(k\)-te Element dieser Ordnung, dann ist \(E(10000)\) gesucht.

Die eigentliche Rechenarbeit besteht darin, die Radikale für alle \(100{,}000\) Zahlen effizient zu bestimmen. Die Implementierungen faktorisieren daher nicht jede Zahl einzeln, sondern berechnen alle Werte in einem siebartigen Durchlauf und sortieren anschließend genau einmal.

Mathematischer Ansatz

Die Struktur der Aufgabe ist sehr konkret: erst alle Werte \(\operatorname{rad}(n)\) berechnen, dann die Paare sortieren und die gewünschte Position ablesen. Die Mathematik steckt vor allem in der Frage, wie man die vollständige Radikaltabelle effizient aufbaut.

Die Ordnung, die \(E(k)\) definiert

Wir schreiben

$$a \prec b \quad\Longleftrightarrow\quad \bigl(\operatorname{rad}(a),a\bigr) \lt \bigl(\operatorname{rad}(b),b\bigr)$$

in lexikographischer Ordnung. Dann ist \(E(1),E(2),\dots,E(100000)\) genau die Folge der Zahlen \(1,2,\dots,100000\), sortiert nach \(\prec\). Das ist wichtig, weil Gleichstände nicht beliebig aufgelöst werden: Haben zwei Zahlen dasselbe Radikal, dann steht die kleinere Zahl zuerst.

Radikale ignorieren Exponenten

Gilt

$$n=\prod_{i=1}^{r} p_i^{e_i},$$

so folgt

$$\operatorname{rad}(n)=\prod_{i=1}^{r} p_i.$$

Das Radikal merkt sich also nur, welche Primzahlen \(n\) teilen, nicht aber ihre Exponenten. Deshalb haben \(2\), \(4\), \(8\) und \(16\) alle das Radikal \(2\), und \(3\) sowie \(9\) beide das Radikal \(3\). Genau dadurch rutschen Potenzen kleiner Primzahlen in der geordneten Liste weit nach vorne. Außerdem gilt stets

$$\operatorname{rad}(n)\le n,$$

wobei Gleichheit genau für quadratfreie Zahlen eintritt.

Ein Sieb, das jede Primzahl genau einmal einmultipliziert

Die Schlüsselerkenntnis lautet: Das Radikal einer Zahl entsteht, indem man alle verschiedenen Primteiler genau einmal multipliziert. Man kann also ein Array \(R\) mit \(R[n]=1\) initialisieren und dann für jede Primzahl \(p \le N\) alle Vielfachen von \(p\) mit \(p\) multiplizieren:

$$R[m]\leftarrow R[m]\cdot p \qquad \text{für } m=p,2p,3p,\dots.$$

Weil die Schleife in Schritten von \(p\) läuft, bekommt jedes Vielfache den Faktor \(p\) genau einmal, ganz gleich ob \(p\) nur einmal oder mehrfach teilt. Nachdem alle Primzahlen verarbeitet wurden, ist daher

$$R[m]=\prod_{p\mid m} p=\operatorname{rad}(m).$$

Die Primzahlerkennung geschieht ebenfalls durch ein Sieb: Eine boolesche Tabelle markiert zusammengesetzte Zahlen, sobald sie als nichttriviale Vielfache einer kleineren Primzahl auftauchen. Erreicht die äußere Schleife einen noch unmarkierten Wert, dann ist dieser Wert prim und muss durch seine Vielfachen propagiert werden.

Warum das Sieb korrekt ist

Ein einfaches Invarianzargument genügt. Nachdem alle Primzahlen bis zu einer Schranke \(q\) verarbeitet wurden, ist \(R[n]\) genau das Produkt der Primteiler von \(n\), die höchstens \(q\) sind. Anfangs stimmt das wegen des leeren Produkts. Wird die nächste Primzahl \(p\) behandelt, dann werden genau die Vielfachen von \(p\) aktualisiert; sie erhalten also genau den noch fehlenden Faktor \(p\), alle anderen Werte bleiben korrekt. Nach Induktion ist nach der letzten Primzahl für jedes \(n\) das vollständige Radikal vorhanden.

Durchgerechnetes Beispiel: der Bereich \(1\) bis \(10\)

Für die ersten zehn Zahlen gelten die Radikale

$$\operatorname{rad}(1)=1,\ \operatorname{rad}(2)=2,\ \operatorname{rad}(3)=3,\ \operatorname{rad}(4)=2,\ \operatorname{rad}(5)=5,$$

$$\operatorname{rad}(6)=6,\ \operatorname{rad}(7)=7,\ \operatorname{rad}(8)=2,\ \operatorname{rad}(9)=3,\ \operatorname{rad}(10)=10.$$

Sortiert man die Paare \((\operatorname{rad}(n),n)\), so erhält man

$$ (1,1),\ (2,2),\ (2,4),\ (2,8),\ (3,3),\ (3,9),\ (5,5),\ (6,6),\ (7,7),\ (10,10). $$

Damit folgt

$$E(4)=8.$$

Das Beispiel zeigt beide Aspekte der Ordnung: \(4\) und \(8\) liegen früh, weil ihr Radikal nur \(2\) ist, und innerhalb einer Gruppe mit gleichem Radikal entscheidet die kleinere Zahl.

Von der sortierten Liste zum gesuchten Wert

Sobald alle Radikale bekannt sind, bleibt nur noch der Ordnungsschritt. Man erstellt die sortierbare Sammlung, ordnet sie lexikographisch nach \((\operatorname{rad}(n),n)\) und liest das Element an Position \(10000\) ab. Es gibt keine weitere Rekursion und keinen versteckten Suchraum; die gesamte Lösung besteht aus dieser Tabelle plus einer Sortierung.

Wie der Code arbeitet

Die C++-, Python- und Java-Implementierungen verwenden alle denselben dreistufigen Ablauf.

Aufbau der Radikaltabelle

Ein Array der Länge \(N+1\) wird mit \(1\) gefüllt. Ein zweites boolesches Array dient dazu, Primzahlen während des Durchlaufs von \(2\) bis \(N\) zu erkennen. Ist der aktuelle Wert noch unmarkiert, so ist er prim; die Implementierung multipliziert ihn in alle Vielfachen ein und markiert die nichttrivialen Vielfachen als zusammengesetzt.

Sortieren nach dem exakten Problemschlüssel

Nach dem Sieb enthält die Implementierung alle Zahlen \(1\) bis \(100{,}000\) zusammen mit ihren Radikalen, oder sie sortiert die Zahlen direkt mit dem Schlüssel \((\operatorname{rad}(n),n)\). Der zweite Schlüssel ist unverzichtbar, weil viele verschiedene Zahlen dasselbe Radikal besitzen.

Auslesen der Zielposition

Die gesuchte Antwort ist das Element an Position \(10000\) bei 1-basierter Zählung, also Index \(9999\) in einer 0-basierten Datenstruktur. Eine der Implementierungen prüft zusätzlich das kleine Beispiel oben und bestätigt so, dass für die Grenze \(10\) tatsächlich \(E(4)=8\) gilt.

Komplexitätsanalyse

Mit \(N=100{,}000\) kostet die Siebphase

$$\sum_{p\le N}\left\lfloor \frac{N}{p}\right\rfloor = O\!\left(N\sum_{p\le N}\frac{1}{p}\right)=O(N\log\log N),$$

weil jedes Vielfache jeder Primzahl genau einmal besucht wird. Das Sortieren von \(N\) Objekten nach \((\operatorname{rad}(n),n)\) benötigt \(O(N\log N)\) und dominiert damit die Gesamtlaufzeit. Der Speicherverbrauch ist \(O(N)\): ein Array für die Radikale, ein boolesches Siebarray und eine sortierbare Sammlung der Zahlen oder Paare.

Fußnoten und Referenzen

  1. Project Euler Problem 124: https://projecteuler.net/problem=124
  2. Radical of an integer: Wikipedia - Radical of an integer
  3. Sieve of Eratosthenes: Wikipedia - Sieve of Eratosthenes
  4. Square-free integer: Wikipedia - Square-free integer
  5. Lexicographic order: Wikipedia - Lexicographic order

Problem Özeti

Her pozitif tam sayı \(n\) için

$$\operatorname{rad}(n)=\prod_{p\mid n} p$$

tanımını yapalım; yani \(\operatorname{rad}(n)\), \(n\)'nin farklı asal bölenlerinin çarpımıdır ve \(\operatorname{rad}(1)=1\) kabul edilir. Problem, \(1 \le n \le 100{,}000\) aralığındaki tüm sayıları \((\operatorname{rad}(n),n)\) çiftine göre sıralar: önce radikal değeri küçük olan gelir, eşitlikte küçük sayı öne geçer. Bu sıralamadaki \(k\). elemana \(E(k)\) denirse amaç \(E(10000)\)'i bulmaktır.

Esas hesap yükü, \(100{,}000\) sayının tamamı için radikal değerlerini üretmektir. Uygulamalar her sayıyı ayrı ayrı asal çarpanlarına ayırmaz. Bunun yerine tüm radikalleri tek bir elek benzeri geçişte hesaplar ve ardından problemde istenen düzene göre bir kez sıralama yapar.

Matematiksel Yaklaşım

Bu problemde yapı nettir: önce tüm \(\operatorname{rad}(n)\) değerlerini kur, sonra çiftleri sırala ve hedef konumu oku. Matematiksel fikir, bu radikal tablosunu gereksiz tekrar yapmadan üretmektir.

\(E(k)\) sıralaması tam olarak nedir?

Şu bağıntıyı tanımlayalım:

$$a \prec b \quad\Longleftrightarrow\quad \bigl(\operatorname{rad}(a),a\bigr) \lt \bigl(\operatorname{rad}(b),b\bigr)$$

burada karşılaştırma leksikografiktir. O zaman \(E(1),E(2),\dots,E(100000)\), \(1,2,\dots,100000\) listesinin \(\prec\) düzenine göre sıralanmış halidir. Bu tanım önemlidir; çünkü eşit radikaller arasında bağ koparma rastgele değildir. \(\operatorname{rad}(a)=\operatorname{rad}(b)\) ise küçük olan sayı önce gelir.

Radikal üslere bakmaz

Eğer

$$n=\prod_{i=1}^{r} p_i^{e_i}$$

ise

$$\operatorname{rad}(n)=\prod_{i=1}^{r} p_i$$

olur. Yani radikal, hangi asalların bölen olduğunu hatırlar ama her asalın kaç kez geçtiğini unutur. Bu yüzden \(2\), \(4\), \(8\) ve \(16\) için radikal hep \(2\)'dir; aynı şekilde \(3\) ile \(9\) için de radikal \(3\)'tür. Küçük asalların kuvvetleri bu nedenle sıralamada beklenenden erken görünür. Ayrıca

$$\operatorname{rad}(n)\le n$$

her zaman doğrudur; eşitlik tam olarak \(n\) karesiz olduğunda sağlanır.

Her asalı tam bir kez çarpan elek

Ana gözlem şudur: Bir sayının radikali, onu bölen farklı asal sayıların her birinin bir kez çarpılmasıyla elde edilir. Bu nedenle önce tüm \(n\) için \(R[n]=1\) yazılır, sonra her asal \(p \le N\) için \(p\)'nin bütün katları \(p\) ile çarpılır:

$$R[m]\leftarrow R[m]\cdot p \qquad \text{burada } m=p,2p,3p,\dots.$$

Döngü \(p\) adımlarıyla ilerlediği için her kat, \(p\) çarpanını tam bir kez alır; \(p^2\) ya da \(p^3\) bölse bile tekrar çarpılmaz. Bütün asallar işlendiğinde

$$R[m]=\prod_{p\mid m} p=\operatorname{rad}(m)$$

elde edilir. Uygulamaların asal tanıması da eleğe dayanır: Daha küçük bir asalin asal olmayan bir katı olarak görülen her sayı işaretlenir. Dış döngü işaretlenmemiş bir değere geldiğinde o değer asaldır ve katlarına yayılmalıdır.

Eleğin neden doğru olduğu

Doğruluk için basit bir değişmez yeterlidir. \(q\)'ya kadar olan tüm asallar işlendiğinde, \(R[n]\) değeri \(n\)'yi bölen ve \(q\)'yu aşmayan asalların çarpımına eşittir. Başlangıçta bu, boş çarpım yüzünden doğrudur. Sonraki asal \(p\) işlendiğinde yalnızca \(p\)'nin katları güncellenir; dolayısıyla bu sayılar eksik olan \(p\) çarpanını alır, diğerleri ise doğru kalır. Tümevarım sonunda tüm \(n\) için tam radikal elde edilmiş olur.

Çalışılmış örnek: \(1\) ile \(10\) arası

İlk on sayı için radikaller

$$\operatorname{rad}(1)=1,\ \operatorname{rad}(2)=2,\ \operatorname{rad}(3)=3,\ \operatorname{rad}(4)=2,\ \operatorname{rad}(5)=5,$$

$$\operatorname{rad}(6)=6,\ \operatorname{rad}(7)=7,\ \operatorname{rad}(8)=2,\ \operatorname{rad}(9)=3,\ \operatorname{rad}(10)=10$$

şeklindedir. \((\operatorname{rad}(n),n)\) çiftlerini sıralarsak

$$ (1,1),\ (2,2),\ (2,4),\ (2,8),\ (3,3),\ (3,9),\ (5,5),\ (6,6),\ (7,7),\ (10,10) $$

elde edilir. Buradan

$$E(4)=8$$

çıkar. Bu küçük örnek sıralamanın iki parçasını da açıkça gösterir: \(4\) ve \(8\) küçük radikalleri nedeniyle erkene gelir; eşit radikal grubunda ise küçük sayı öndedir.

Sıralı listeden hedef terime geçmek

Tüm radikaller hazır olduktan sonra problem artık saf bir sıralama problemidir. Sıralanabilir koleksiyon kurulup \((\operatorname{rad}(n),n)\) anahtarına göre leksikografik olarak sıralanır ve \(10000\). eleman okunur. Yani çözüm, gizli bir özyineleme ya da karmaşık bir arama değil; radikal tablosu ile tek bir sıralamadan ibarettir.

Kod Nasıl Çalışır

C++, Python ve Java uygulamalarının izlediği algoritma aynı üç aşamadan oluşur.

Radikal tablosunun kurulması

Uzunluğu \(N+1\) olan bir dizi \(1\) ile başlatılır. İkinci bir boole dizisi, \(2\)'den \(N\)'e kadar gidilirken asalları ayırt etmek için kullanılır. Güncel değer hâlâ işaretlenmemişse asaldır; uygulama bu asalı bütün katlara çarpar ve \(p\)'nin kendisi dışındaki katları bileşik olarak işaretler.

\((\operatorname{rad}(n),n)\) anahtarına göre sıralama

Elek bittikten sonra uygulama \(1\) ile \(100{,}000\) arasındaki bütün sayıları radikalleriyle birlikte tutan bir koleksiyon oluşturur; eşdeğer olarak sayıları doğrudan \((\operatorname{rad}(n),n)\) anahtarıyla sıralar. İkinci anahtar zorunludur, çünkü birçok farklı sayı aynı radikal değerini paylaşır.

\(10000\). elemanı seçme

İstenen sonuç, 1-bazlı sayımda \(10000\). konumdaki elemandır; bu da 0-bazlı depolamada indeks \(9999\) anlamına gelir. Uygulamalardan biri ayrıca yukarıdaki küçük örneği kontrol ederek sınır \(10\) iken gerçekten \(E(4)=8\) çıktığını doğrular.

Karmaşıklık Analizi

\(N=100{,}000\) için elek aşamasının maliyeti

$$\sum_{p\le N}\left\lfloor \frac{N}{p}\right\rfloor = O\!\left(N\sum_{p\le N}\frac{1}{p}\right)=O(N\log\log N)$$

olur; çünkü her asal için onun katları bir kez ziyaret edilir. \((\operatorname{rad}(n),n)\) anahtarına göre \(N\) öğeyi sıralamak \(O(N\log N)\) zaman alır ve toplam sürenin baskın kısmıdır. Bellek kullanımı \(O(N)\)'dir: bir radikal dizisi, bir boole işaretleme dizisi ve sayıları ya da çiftleri tutan sıralanabilir yapı gerekir.

Dipnotlar ve Kaynaklar

  1. Project Euler Problem 124: https://projecteuler.net/problem=124
  2. Radical of an integer: Wikipedia - Radical of an integer
  3. Sieve of Eratosthenes: Wikipedia - Sieve of Eratosthenes
  4. Square-free integer: Wikipedia - Square-free integer
  5. Lexicographic order: Wikipedia - Lexicographic order

Resumen del Problema

Para cada entero positivo \(n\), definimos

$$\operatorname{rad}(n)=\prod_{p\mid n} p,$$

el producto de los divisores primos distintos de \(n\), con \(\operatorname{rad}(1)=1\). El problema ordena todos los enteros \(1 \le n \le 100{,}000\) por el par \((\operatorname{rad}(n),n)\): primero por radical, y en caso de empate por el propio valor de \(n\). Si \(E(k)\) denota el elemento que ocupa la posición \(k\) en esa lista ordenada, hay que determinar \(E(10000)\).

La dificultad real no está en la definición del orden, sino en calcular los \(100{,}000\) valores de \(\operatorname{rad}(n)\) de manera eficiente. Las implementaciones no factorizan cada número por separado; construyen toda la tabla de radicales con una pasada tipo tamiz y luego realizan una única ordenación.

Enfoque Matemático

La estructura del problema es concreta: calcular todos los radicales, ordenar los pares y leer una posición. La parte matemática consiste en entender por qué el tamiz produce exactamente la función radical para todos los enteros del rango.

La ordenación que define \(E(k)\)

Escribimos

$$a \prec b \quad\Longleftrightarrow\quad \bigl(\operatorname{rad}(a),a\bigr) \lt \bigl(\operatorname{rad}(b),b\bigr)$$

en orden lexicográfico. Entonces \(E(1),E(2),\dots,E(100000)\) es simplemente la lista de enteros \(1,2,\dots,100000\) ordenada por \(\prec\). Este punto es importante porque los empates no se resuelven de forma arbitraria: si dos números comparten radical, aparece antes el menor de ellos.

El radical elimina exponentes

Si

$$n=\prod_{i=1}^{r} p_i^{e_i},$$

entonces

$$\operatorname{rad}(n)=\prod_{i=1}^{r} p_i.$$

Es decir, el radical solo recuerda qué primos dividen a \(n\), no cuántas veces aparecen. Por eso \(2\), \(4\), \(8\) y \(16\) tienen todos radical \(2\), mientras que \(3\) y \(9\) tienen radical \(3\). Las potencias de primos pequeños avanzan en la clasificación precisamente por esta razón. Además, siempre se cumple

$$\operatorname{rad}(n)\le n,$$

con igualdad exacta cuando \(n\) es libre de cuadrados.

Un tamiz que multiplica cada primo una sola vez

La idea central es que el radical de \(m\) se obtiene multiplicando una vez cada primo que divide a \(m\). Por ello se inicializa un arreglo \(R\) con \(R[n]=1\) para todo \(n\), y para cada primo \(p \le N\) se multiplica \(p\) en todos sus múltiplos:

$$R[m]\leftarrow R[m]\cdot p \qquad \text{para } m=p,2p,3p,\dots.$$

Como el recorrido avanza de \(p\) en \(p\), cada múltiplo recibe el factor \(p\) exactamente una vez, aunque \(p^2\) o \(p^3\) también lo dividan. Al terminar el barrido de todos los primos, queda

$$R[m]=\prod_{p\mid m} p=\operatorname{rad}(m).$$

La detección de primos también se hace dentro del tamiz: un arreglo booleano marca como compuestos los múltiplos no triviales de primos menores. Si el bucle exterior llega a un valor todavía no marcado, ese valor es primo y debe propagarse a sus múltiplos.

Por qué el tamiz es correcto

La corrección se demuestra con un invariante sencillo. Después de procesar todos los primos hasta cierto umbral \(q\), el valor \(R[n]\) es el producto de los divisores primos de \(n\) que no exceden \(q\). Al inicio esto vale por tratarse de un producto vacío. Cuando se procesa el siguiente primo \(p\), solo se actualizan los múltiplos de \(p\), que son exactamente los números que necesitan incorporar ese nuevo factor. Por inducción, al final del proceso \(R[n]\) coincide con \(\operatorname{rad}(n)\) para todo \(n\).

Ejemplo trabajado: del \(1\) al \(10\)

Para los diez primeros enteros, los radicales son

$$\operatorname{rad}(1)=1,\ \operatorname{rad}(2)=2,\ \operatorname{rad}(3)=3,\ \operatorname{rad}(4)=2,\ \operatorname{rad}(5)=5,$$

$$\operatorname{rad}(6)=6,\ \operatorname{rad}(7)=7,\ \operatorname{rad}(8)=2,\ \operatorname{rad}(9)=3,\ \operatorname{rad}(10)=10.$$

Al ordenar los pares \((\operatorname{rad}(n),n)\) obtenemos

$$ (1,1),\ (2,2),\ (2,4),\ (2,8),\ (3,3),\ (3,9),\ (5,5),\ (6,6),\ (7,7),\ (10,10). $$

Por tanto,

$$E(4)=8.$$

Este ejemplo deja ver claramente las dos reglas del problema: \(4\) y \(8\) aparecen pronto porque su radical es pequeño, y dentro de un mismo radical se conserva el orden natural de los enteros.

De la lista ordenada a la respuesta

Una vez calculados todos los radicales, lo que queda es puramente una operación de ordenación. Se construye la colección sortable, se ordena lexicográficamente por \((\operatorname{rad}(n),n)\) y se toma el elemento situado en la posición \(10000\). No hay recurrencias ocultas ni búsqueda adicional más allá del tamiz y la ordenación.

Cómo Funciona el Código

Las implementaciones en C++, Python y Java siguen el mismo algoritmo en tres fases.

Construcción de la tabla de radicales

Se inicializa un arreglo de tamaño \(N+1\) con unos. Un segundo arreglo booleano se usa para reconocer números primos mientras se recorre de \(2\) a \(N\). Cuando el valor actual sigue sin marcar, es primo; entonces se multiplica en todos sus múltiplos y se marcan como compuestos los múltiplos mayores que el propio primo.

Ordenación por la clave exacta del problema

Tras el tamiz, la implementación construye una colección con todos los enteros de \(1\) a \(100{,}000\) y sus radicales, o bien ordena directamente los enteros con la clave \((\operatorname{rad}(n),n)\). La segunda componente es imprescindible, porque muchos valores comparten el mismo radical.

Extracción del elemento \(10000\)

La respuesta final es el elemento en la posición \(10000\) con numeración desde \(1\), es decir, el índice \(9999\) si la estructura está indexada desde \(0\). Una de las implementaciones además verifica el ejemplo pequeño anterior y comprueba que, con límite \(10\), se obtiene \(E(4)=8\).

Análisis de Complejidad

Con \(N=100{,}000\), el coste del tamiz es

$$\sum_{p\le N}\left\lfloor \frac{N}{p}\right\rfloor = O\!\left(N\sum_{p\le N}\frac{1}{p}\right)=O(N\log\log N),$$

porque se visita cada múltiplo de cada primo una sola vez. Ordenar \(N\) elementos por \((\operatorname{rad}(n),n)\) requiere \(O(N\log N)\) y domina el tiempo total. El uso de memoria es \(O(N)\): una tabla de radicales, un arreglo booleano para el estado del tamiz y una colección sortable con los enteros o con los pares.

Notas y Referencias

  1. Project Euler Problem 124: https://projecteuler.net/problem=124
  2. Radical of an integer: Wikipedia - Radical of an integer
  3. Sieve of Eratosthenes: Wikipedia - Sieve of Eratosthenes
  4. Square-free integer: Wikipedia - Square-free integer
  5. Lexicographic order: Wikipedia - Lexicographic order

问题概述

对每个正整数 \(n\),定义

$$\operatorname{rad}(n)=\prod_{p\mid n} p,$$

也就是 \(n\) 的所有不同质因子的乘积,并规定 \(\operatorname{rad}(1)=1\)。题目要求把所有 \(1 \le n \le 100{,}000\) 按照二元组 \((\operatorname{rad}(n),n)\) 排序:先比较 radical 值,若相同再比较 \(n\) 本身。记这个有序序列的第 \(k\) 个元素为 \(E(k)\),目标就是求出 \(E(10000)\)。

真正需要处理的不是排序规则本身,而是如何高效得到全部 \(100{,}000\) 个 radical 值。实现并没有把每个整数单独分解质因数,而是用一次类似筛法的遍历同时构造整张 radical 表,然后再做一次符合题意的排序。

数学方法

这道题的结构很直接:先为所有整数计算 \(\operatorname{rad}(n)\),再对对应的二元组排序,最后读取目标位置。数学上的关键点在于,为什么那个筛法恰好会把每个整数的 radical 正确算出来。

\(E(k)\) 的排序对象

定义关系

$$a \prec b \quad\Longleftrightarrow\quad \bigl(\operatorname{rad}(a),a\bigr) \lt \bigl(\operatorname{rad}(b),b\bigr)$$

其中右边是通常的字典序比较。这样一来,\(E(1),E(2),\dots,E(100000)\) 就是把整数 \(1,2,\dots,100000\) 按照 \(\prec\) 排好的结果。这个定义不能简化成“只按 radical 排序”,因为相同 radical 的数必须按原整数大小继续排,这正是题目要求的次关键字。

radical 函数只保留不同质因子

$$n=\prod_{i=1}^{r} p_i^{e_i},$$

那么

$$\operatorname{rad}(n)=\prod_{i=1}^{r} p_i.$$

也就是说,radical 只关心“哪些质数出现过”,并不关心各自出现了多少次。于是 \(2\)、\(4\)、\(8\)、\(16\) 的 radical 都等于 \(2\);\(3\) 和 \(9\) 的 radical 都等于 \(3\)。这就解释了为什么一些含有高次幂的小质数的数会在排序中明显提前。它还给出一个基本性质:

$$\operatorname{rad}(n)\le n,$$

并且只有当 \(n\) 是平方自由数时才取等号。

把每个质数乘进去一次的筛法

核心观察是:如果一个质数 \(p\) 整除 \(m\),那么在 \(\operatorname{rad}(m)\) 中只需要贡献一个因子 \(p\)。因此可以先把数组 \(R\) 初始化为 \(R[n]=1\),然后对每个质数 \(p \le N\),把 \(p\) 乘到它的所有倍数上:

$$R[m]\leftarrow R[m]\cdot p \qquad \text{其中 } m=p,2p,3p,\dots.$$

因为循环是按步长 \(p\) 前进的,所以每个倍数只会被这个质数更新一次;即使 \(p^2\)、\(p^3\) 也整除它,也不会重复乘入。等所有质数都处理完以后,就有

$$R[m]=\prod_{p\mid m} p=\operatorname{rad}(m).$$

实现里判断“当前数是不是质数”的方法也来自筛法本身:一个布尔数组会在某个数作为更小质数的非平凡倍数出现时把它标记为合数。于是外层循环第一次遇到未标记的数时,就可以确定它是质数,并把它传播到所有倍数上。

为什么这个筛法是正确的

可以用一个很自然的不变式来说明。设已经处理完所有不超过 \(q\) 的质数,那么此时 \(R[n]\) 正好等于 \(n\) 的那些不超过 \(q\) 的质因子的乘积。开始时这是空积,因此为 \(1\)。接下来处理下一个质数 \(p\) 时,只有 \(p\) 的倍数会被更新,而这些数恰好都缺少一个因子 \(p\);其他数不需要改变。由归纳法可知,在所有质数都处理完以后,\(R[n]\) 就是完整的 \(\operatorname{rad}(n)\)。

从 \(1\) 到 \(10\) 的具体例子

前十个整数的 radical 为

$$\operatorname{rad}(1)=1,\ \operatorname{rad}(2)=2,\ \operatorname{rad}(3)=3,\ \operatorname{rad}(4)=2,\ \operatorname{rad}(5)=5,$$

$$\operatorname{rad}(6)=6,\ \operatorname{rad}(7)=7,\ \operatorname{rad}(8)=2,\ \operatorname{rad}(9)=3,\ \operatorname{rad}(10)=10.$$

按 \((\operatorname{rad}(n),n)\) 排序后得到

$$ (1,1),\ (2,2),\ (2,4),\ (2,8),\ (3,3),\ (3,9),\ (5,5),\ (6,6),\ (7,7),\ (10,10). $$

因此

$$E(4)=8.$$

这个例子把题目的两个规则都展示得很清楚:\(4\) 和 \(8\) 因为 radical 很小而被提前;而在 radical 相同的组内,又按照整数本身的大小继续排列。

由有序列表读出答案

一旦所有 radical 都已经算出,剩下的工作就纯粹是排序。构造可排序的数据集合,按照 \((\operatorname{rad}(n),n)\) 做字典序排序,然后读取第 \(10000\) 个位置即可。也就是说,这道题没有隐藏的递推或搜索树,完整解法就是“整表筛出 radical,再排序一次”。

代码如何工作

C++、Python 和 Java 实现都遵循同样的三步算法。

先构造全部 radical 值

程序先建立一个长度为 \(N+1\) 的数组,并把每个位置初始化为 \(1\)。另一个布尔数组用于在从 \(2\) 扫到 \(N\) 的过程中识别质数。如果当前位置还没有被标记,它就是质数;程序就把这个质数乘到所有倍数上,并把大于该质数本身的倍数标记为合数。

按 \((\operatorname{rad}(n),n)\) 排序

筛法结束后,程序要么显式构造“整数加 radical”的二元组集合,要么直接把整数按照键 \((\operatorname{rad}(n),n)\) 排序。第二关键字绝不能省略,因为许多不同整数会共享同一个 radical。

取第 \(10000\) 个元素

最终答案是 1 开始计数时的第 \(10000\) 个元素;若内部使用 0 开始计数的数组,对应的就是下标 \(9999\)。其中一个实现还额外检查了上面的微型例子,确认当上界为 \(10\) 时确实有 \(E(4)=8\)。

复杂度分析

令 \(N=100{,}000\)。筛法阶段的时间为

$$\sum_{p\le N}\left\lfloor \frac{N}{p}\right\rfloor = O\!\left(N\sum_{p\le N}\frac{1}{p}\right)=O(N\log\log N),$$

因为每个质数的每个倍数只会被访问一次。随后对 \(N\) 个对象按 \((\operatorname{rad}(n),n)\) 排序,代价是 \(O(N\log N)\),这也是总时间中的主导项。空间复杂度为 \(O(N)\):需要一张 radical 表、一个布尔标记数组,以及保存整数或二元组的可排序集合。

注释与参考资料

  1. Project Euler Problem 124: https://projecteuler.net/problem=124
  2. Radical of an integer: Wikipedia - Radical of an integer
  3. Sieve of Eratosthenes: Wikipedia - Sieve of Eratosthenes
  4. Square-free integer: Wikipedia - Square-free integer
  5. Lexicographic order: Wikipedia - Lexicographic order

Краткое содержание задачи

Для каждого положительного целого \(n\) определим

$$\operatorname{rad}(n)=\prod_{p\mid n} p,$$

то есть произведение различных простых делителей числа \(n\), причём \(\operatorname{rad}(1)=1\). В задаче все числа \(1 \le n \le 100{,}000\) упорядочиваются по паре \((\operatorname{rad}(n),n)\): сначала по радикалу, а при равенстве по самому числу. Если \(E(k)\) обозначает \(k\)-й элемент этого порядка, требуется найти \(E(10000)\).

Главная вычислительная часть состоит в том, чтобы быстро получить значения \(\operatorname{rad}(n)\) сразу для всех \(100{,}000\) чисел. Реализации не раскладывают каждое число по отдельности, а строят всю таблицу радикалов одним проходом типа решета, после чего выполняют ровно одну сортировку.

Математический подход

Структура решения очень конкретна: сначала вычислить все \(\operatorname{rad}(n)\), затем отсортировать пары и прочитать нужную позицию. Математическое содержание заключается в том, почему выбранное решето действительно строит радикал для каждого числа диапазона.

Как определяется \(E(k)\)

Введём отношение

$$a \prec b \quad\Longleftrightarrow\quad \bigl(\operatorname{rad}(a),a\bigr) \lt \bigl(\operatorname{rad}(b),b\bigr)$$

в лексикографическом смысле. Тогда последовательность \(E(1),E(2),\dots,E(100000)\) есть просто числа \(1,2,\dots,100000\), отсортированные по \(\prec\). Важно, что при равенстве радикалов порядок не произволен: раньше идёт меньшее число.

Радикал отбрасывает степени

Если

$$n=\prod_{i=1}^{r} p_i^{e_i},$$

то

$$\operatorname{rad}(n)=\prod_{i=1}^{r} p_i.$$

Иначе говоря, радикал запоминает только набор простых делителей, но не их кратности. Поэтому числа \(2\), \(4\), \(8\) и \(16\) имеют один и тот же радикал \(2\), а \(3\) и \(9\) оба имеют радикал \(3\). Именно поэтому степени маленьких простых чисел оказываются в упорядоченном списке заметно раньше. Кроме того, всегда выполняется

$$\operatorname{rad}(n)\le n,$$

и равенство имеет место тогда и только тогда, когда число квадратсвободно.

Решето, которое умножает каждый простой ровно один раз

Ключевое наблюдение состоит в том, что \(\operatorname{rad}(m)\) получается умножением всех различных простых делителей \(m\) по одному разу. Поэтому можно завести массив \(R\) со значениями \(R[n]=1\), а затем для каждого простого \(p \le N\) умножать \(p\) во все кратные \(p\):

$$R[m]\leftarrow R[m]\cdot p \qquad \text{для } m=p,2p,3p,\dots.$$

Поскольку цикл идёт шагом \(p\), каждое кратное получает множитель \(p\) ровно один раз, даже если \(p^2\) или \(p^3\) тоже делят это число. После обработки всех простых чисел получаем

$$R[m]=\prod_{p\mid m} p=\operatorname{rad}(m).$$

Проверка простоты в реализациях тоже встроена в решето: булев массив отмечает составные числа, как только они появляются в качестве нетривиальных кратных меньшего простого. Поэтому всякий неотмеченный элемент внешнего цикла является простым и должен быть распространён по своим кратным.

Почему решето корректно

Корректность удобно доказывать через инвариант. После обработки всех простых чисел, не превосходящих \(q\), значение \(R[n]\) равно произведению тех простых делителей числа \(n\), которые не больше \(q\). В начале это верно как пустое произведение. Когда обрабатывается следующий простой \(p\), обновляются только кратные \(p\), то есть именно те числа, которым не хватает множителя \(p\). Остальные элементы остаются верными. Индукция завершает доказательство: после последнего простого числа в \(R[n]\) находится полный радикал.

Разобранный пример: от \(1\) до \(10\)

Для первых десяти целых радикалы равны

$$\operatorname{rad}(1)=1,\ \operatorname{rad}(2)=2,\ \operatorname{rad}(3)=3,\ \operatorname{rad}(4)=2,\ \operatorname{rad}(5)=5,$$

$$\operatorname{rad}(6)=6,\ \operatorname{rad}(7)=7,\ \operatorname{rad}(8)=2,\ \operatorname{rad}(9)=3,\ \operatorname{rad}(10)=10.$$

Если отсортировать пары \((\operatorname{rad}(n),n)\), получится

$$ (1,1),\ (2,2),\ (2,4),\ (2,8),\ (3,3),\ (3,9),\ (5,5),\ (6,6),\ (7,7),\ (10,10). $$

Следовательно,

$$E(4)=8.$$

Этот пример показывает обе части правила: \(4\) и \(8\) поднимаются вверх из-за малого радикала, а внутри группы с одинаковым радикалом сохраняется порядок по самому числу.

Как получить ответ после сортировки

После того как все радикалы вычислены, остаётся только упорядочить данные по ключу \((\operatorname{rad}(n),n)\) и взять элемент в позиции \(10000\). Никакой скрытой рекурсии или дополнительного перебора здесь нет; всё решение полностью состоит из построения таблицы радикалов и одной сортировки.

Как работает код

Реализации на C++, Python и Java используют один и тот же трёхшаговый алгоритм.

Построение таблицы радикалов

Сначала создаётся массив длины \(N+1\), заполненный единицами. Второй булев массив нужен, чтобы распознавать простые числа во время прохода от \(2\) до \(N\). Если текущее значение ещё не отмечено, оно простое; тогда программа умножает его во все кратные и помечает кратные, большие самого простого, как составные.

Сортировка по ключу \((\operatorname{rad}(n),n)\)

После завершения решета реализация либо явно хранит пары «радикал и число», либо напрямую сортирует числа от \(1\) до \(100{,}000\) по ключу \((\operatorname{rad}(n),n)\). Второй компонент обязателен, потому что у разных чисел радикалы часто совпадают.

Извлечение \(10000\)-го элемента

Итоговый ответ находится на позиции \(10000\) при нумерации с единицы, то есть по индексу \(9999\) при нумерации с нуля. Одна из реализаций дополнительно проверяет приведённый выше мини-пример и убеждается, что при верхней границе \(10\) действительно получается \(E(4)=8\).

Анализ сложности

При \(N=100{,}000\) время работы решета равно

$$\sum_{p\le N}\left\lfloor \frac{N}{p}\right\rfloor = O\!\left(N\sum_{p\le N}\frac{1}{p}\right)=O(N\log\log N),$$

потому что каждое кратное каждого простого посещается один раз. Сортировка \(N\) объектов по \((\operatorname{rad}(n),n)\) требует \(O(N\log N)\) и доминирует в общей асимптотике. Память имеет порядок \(O(N)\): нужен массив радикалов, булев массив для состояния решета и sortable-структура с числами или парами.

Примечания и ссылки

  1. Project Euler Problem 124: https://projecteuler.net/problem=124
  2. Radical of an integer: Wikipedia - Radical of an integer
  3. Sieve of Eratosthenes: Wikipedia - Sieve of Eratosthenes
  4. Square-free integer: Wikipedia - Square-free integer
  5. Lexicographic order: Wikipedia - Lexicographic order

ملخص المسألة

لكل عدد صحيح موجب \(n\) نعرّف

$$\operatorname{rad}(n)=\prod_{p\mid n} p,$$

أي حاصل ضرب القواسم الأولية المميزة للعدد \(n\)، مع الاتفاق على أن \(\operatorname{rad}(1)=1\). ترتّب المسألة جميع الأعداد \(1 \le n \le 100{,}000\) وفق الزوج \((\operatorname{rad}(n),n)\): الأصغر في القيمة الجذرية أولاً، وعند التساوي يأتي العدد الأصغر أولاً. وإذا رمزنا بالرمز \(E(k)\) إلى العنصر رقم \(k\) في هذا الترتيب، فالمطلوب هو إيجاد \(E(10000)\).

الصعوبة الحقيقية ليست في تعريف الترتيب، بل في حساب قيم \(\operatorname{rad}(n)\) لكل الأعداد حتى \(100{,}000\) بكفاءة. لذلك لا تقوم التطبيقات بتحليل كل عدد على حدة إلى عوامله الأولية، بل تبني جدول القيم كله بمرور واحد شبيه بالغربال، ثم تجري عملية فرز واحدة وفق الترتيب المطلوب تمامًا.

المنهج الرياضي

بنية المسألة مباشرة: نحسب جميع القيم \(\operatorname{rad}(n)\)، ثم نفرز الأزواج، ثم نقرأ الموضع المطلوب. الفكرة الرياضية الأساسية هي فهم لماذا يعطي هذا الغربال بالضبط القيمة الجذرية الصحيحة لكل عدد.

الترتيب الذي يعرّف \(E(k)\)

نكتب

$$a \prec b \quad\Longleftrightarrow\quad \bigl(\operatorname{rad}(a),a\bigr) \lt \bigl(\operatorname{rad}(b),b\bigr)$$

حيث المقارنة في الطرف الأيمن معجمية. وعندئذ تكون السلسلة \(E(1),E(2),\dots,E(100000)\) مجرد الأعداد \(1,2,\dots,100000\) بعد فرزها بهذا الترتيب. وهذه الصياغة مهمة لأن حالات التساوي ليست اعتباطية: إذا كان لعددين القيمة الجذرية نفسها، فإن الأصغر منهما يجب أن يظهر أولاً.

الدالة الجذرية تتجاهل الأسس

إذا كان

$$n=\prod_{i=1}^{r} p_i^{e_i},$$

فإن

$$\operatorname{rad}(n)=\prod_{i=1}^{r} p_i.$$

أي إن الدالة الجذرية تتذكر أي الأعداد الأولية تقسم \(n\)، لكنها لا تهتم بعدد مرات ظهور كل واحد منها. لذلك فالأعداد \(2\) و\(4\) و\(8\) و\(16\) لها جميعًا القيمة الجذرية \(2\)، وكذلك \(3\) و\(9\) لهما القيمة \(3\). وهذا يفسّر لماذا تتقدم قوى الأعداد الأولية الصغيرة في القائمة المرتبة. كذلك لدينا دائمًا

$$\operatorname{rad}(n)\le n,$$

وتتحقق المساواة بالضبط عندما يكون \(n\) خاليًا من المربعات.

غربال يضرب كل عدد أولي مرة واحدة

الملاحظة الأساسية هي أن \(\operatorname{rad}(m)\) ينتج من ضرب جميع الأعداد الأولية المختلفة التي تقسم \(m\) مرة واحدة فقط. لذلك يمكن تهيئة مصفوفة \(R\) بحيث يكون \(R[n]=1\) لجميع \(n\)، ثم لكل عدد أولي \(p \le N\) نضرب \(p\) في كل مضاعف له:

$$R[m]\leftarrow R[m]\cdot p,\qquad m=p,2p,3p,\dots.$$

ولأن الحلقة تسير بخطوة مقدارها \(p\)، فإن كل مضاعف يتلقى العامل \(p\) مرة واحدة فقط، حتى لو كان \(p^2\) أو \(p^3\) يقسمه أيضًا. وبعد معالجة جميع الأعداد الأولية يصبح

$$R[m]=\prod_{p\mid m} p=\operatorname{rad}(m).$$

أما معرفة ما إذا كان العنصر الحالي أوليًا أم لا فتتم داخل الغربال نفسه: توجد مصفوفة منطقية تضع علامة على كل عدد مركب بمجرد أن يظهر كمضاعف غير تافه لعدد أولي أصغر. ولذلك فإن أول قيمة غير معلّمة تصل إليها الحلقة الخارجية تكون عددًا أوليًا بالفعل، ويجب نشرها على جميع مضاعفاتها.

لماذا هذا الغربال صحيح

يمكن إثبات الصحة بثابت بسيط أثناء التنفيذ. بعد معالجة جميع الأعداد الأولية التي لا تتجاوز \(q\)، تكون القيمة \(R[n]\) مساوية لحاصل ضرب القواسم الأولية للعدد \(n\) التي لا تتجاوز \(q\). في البداية هذا صحيح لأنه حاصل ضرب فارغ. وعندما نعالج العدد الأولي التالي \(p\)، فإننا نحدّث فقط مضاعفات \(p\)، وهي بالضبط الأعداد التي كانت تفتقد العامل \(p\). أما بقية القيم فتبقى صحيحة كما هي. وبالاستقراء نحصل في النهاية على \(\operatorname{rad}(n)\) الكامل لكل \(n\).

مثال محلول من \(1\) إلى \(10\)

بالنسبة إلى الأعداد العشرة الأولى تكون القيم الجذرية كما يلي:

$$\operatorname{rad}(1)=1,\ \operatorname{rad}(2)=2,\ \operatorname{rad}(3)=3,\ \operatorname{rad}(4)=2,\ \operatorname{rad}(5)=5,$$

$$\operatorname{rad}(6)=6,\ \operatorname{rad}(7)=7,\ \operatorname{rad}(8)=2,\ \operatorname{rad}(9)=3,\ \operatorname{rad}(10)=10.$$

وعند فرز الأزواج \((\operatorname{rad}(n),n)\) نحصل على

$$ (1,1),\ (2,2),\ (2,4),\ (2,8),\ (3,3),\ (3,9),\ (5,5),\ (6,6),\ (7,7),\ (10,10). $$

ومن ثم

$$E(4)=8.$$

يوضح هذا المثال جانبي الترتيب معًا: فالعددان \(4\) و\(8\) يتقدمان لأن قيمتهما الجذرية صغيرة، وداخل المجموعة ذات القيمة الجذرية نفسها يُحسم الترتيب بقيمة العدد نفسه.

من القائمة المرتبة إلى الجواب

بعد أن تصبح جميع القيم الجذرية معروفة، يتحول ما بقي من المسألة إلى خطوة فرز فقط. نبني المجموعة القابلة للفرز، ثم نفرزها معجميًا بالمفتاح \((\operatorname{rad}(n),n)\)، ثم نقرأ العنصر الموجود في الموضع \(10000\). لذلك فالحل كله ليس أكثر من بناء جدول radical كامل ثم إجراء فرز واحد.

كيف تعمل الشيفرة

تتبع تطبيقات C++ وPython وJava الخطة نفسها المؤلفة من ثلاث مراحل.

بناء جدول القيم الجذرية

يبدأ التنفيذ بمصفوفة طولها \(N+1\) مملوءة بالقيمة \(1\). وتستعمل مصفوفة منطقية ثانية للتمييز بين الأعداد الأولية وغير الأولية أثناء المسح من \(2\) إلى \(N\). فإذا كانت القيمة الحالية غير معلّمة بعد، فهي أولية؛ عندها تضرب الشيفرة هذا العدد في جميع مضاعفاته، وتعلّم المضاعفات الأكبر من العدد نفسه على أنها مركبة.

الفرز بالمفتاح \((\operatorname{rad}(n),n)\)

بعد اكتمال الغربال، تنشئ الشيفرة بنية تحتوي على جميع الأعداد من \(1\) إلى \(100{,}000\) مع قيمها الجذرية، أو ترتب الأعداد مباشرة باستخدام المفتاح \((\operatorname{rad}(n),n)\). والمكوّن الثاني من هذا المفتاح ضروري، لأن كثيرًا من الأعداد المختلفة يشترك في القيمة الجذرية نفسها.

استخراج العنصر رقم \(10000\)

الجواب النهائي هو العنصر الواقع في الموضع \(10000\) عند العد ابتداءً من \(1\)، أي الفهرس \(9999\) إذا كانت البنية مفهرسة ابتداءً من الصفر. كما أن إحدى التطبيقات تتحقق أيضًا من المثال الصغير السابق، فتؤكد أنه عندما يكون الحد الأعلى \(10\) نحصل فعلًا على \(E(4)=8\).

تحليل التعقيد

إذا أخذنا \(N=100{,}000\)، فإن كلفة مرحلة الغربال هي

$$\sum_{p\le N}\left\lfloor \frac{N}{p}\right\rfloor = O\!\left(N\sum_{p\le N}\frac{1}{p}\right)=O(N\log\log N),$$

لأن كل مضاعف لكل عدد أولي يُزار مرة واحدة. وبعد ذلك فإن فرز \(N\) عنصرًا وفق \((\operatorname{rad}(n),n)\) يكلف \(O(N\log N)\)، وهو الحد الغالب في زمن التنفيذ الكلي. أما الذاكرة فهي من الرتبة \(O(N)\): مصفوفة للقيم الجذرية، ومصفوفة منطقية لحالة الغربال، وبنية قابلة للفرز تخزن الأعداد أو الأزواج.

هوامش ومراجع

  1. Project Euler Problem 124: https://projecteuler.net/problem=124
  2. Radical of an integer: Wikipedia - Radical of an integer
  3. Sieve of Eratosthenes: Wikipedia - Sieve of Eratosthenes
  4. Square-free integer: Wikipedia - Square-free integer
  5. Lexicographic order: Wikipedia - Lexicographic order