Problem Summary

A cuboid with side lengths \(a\), \(b\), and \(c\) is wrapped by successive outer layers of unit cubes. If the dimensions are ordered as \(a \le b \le c\) and the layer index is \(\ell \ge 1\), let \(L(a,b,c,\ell)\) be the number of cubes in the \(\ell\)-th layer. The problem asks for the smallest integer \(m\) such that exactly 1000 ordered cuboids-and-layers produce \(m\) cubes.

It is convenient to write the counting function as

$$C(m)=\#\{(a,b,c,\ell): a \le b \le c,\ \ell \ge 1,\ L(a,b,c,\ell)=m\}.$$

The goal is therefore to find the least \(m\) for which \(C(m)=1000\). The ordering \(a \le b \le c\) removes rotational duplicates, but different shapes and different layer numbers are all counted separately.

Mathematical Approach

The implementations are built around one explicit formula for the size of a cuboid layer and one exhaustive counting pass over all admissible states \((a,b,c,\ell)\).

The first layer is just the surface of the cuboid

The outermost layer touching the original cuboid covers its six faces. Two faces have area \(ab\), two have area \(ac\), and two have area \(bc\), so

$$L(a,b,c,1)=2(ab+ac+bc).$$

This is the smallest layer attached to that cuboid, so any search bounded by \(m\) only needs cuboids satisfying \(2(ab+ac+bc) \le m\).

How one layer grows into the next

Moving from layer \(\ell\) to layer \(\ell+1\) adds a one-cube-thick border around the current shell. The linear part comes from the 12 edge bands: each of the three edge lengths appears four times, so the edge contribution is always \(4(a+b+c)\).

There is also a corner contribution. Once the shell is thicker than the first layer, the stepped patches near the eight corners keep widening, and together they contribute \(8(\ell-1)\) new cubes when the layer index increases from \(\ell\) to \(\ell+1\). Therefore

$$L(a,b,c,\ell+1)-L(a,b,c,\ell)=4(a+b+c)+8(\ell-1)=4(a+b+c+2\ell-2).$$

For a fixed cuboid, the successive differences form an arithmetic progression. That monotonicity is exactly why the inner loop over layers can stop as soon as it passes the current search limit.

Summing the recurrence gives the closed form

Starting from \(L(a,b,c,1)=2(ab+ac+bc)\) and summing the previous increment for \(\ell-1\) steps gives

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c)+4(\ell-1)(\ell-2).$$

The last two terms are often combined into

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c+\ell-2).$$

This is the exact formula used by the C++, Python, and Java implementations. Several useful facts are immediate:

\(L(a,b,c,\ell)\) is always even, it is strictly increasing in \(\ell\) for fixed \(a,b,c\), and it is also increasing in each of \(a\), \(b\), and \(c\) when the other variables are fixed.

Worked example: the \(1 \times 2 \times 3\) cuboid

For \(a=1\), \(b=2\), \(c=3\), the first layer contains

$$L(1,2,3,1)=2(1\cdot2+1\cdot3+2\cdot3)=2(11)=22.$$

The recurrence then says

$$L(1,2,3,2)-L(1,2,3,1)=4(1+2+3)=24,$$

so \(L(1,2,3,2)=46\). For the next layer, the increment has grown by 8:

$$L(1,2,3,3)-L(1,2,3,2)=4(1+2+3+2)=32,$$

hence \(L(1,2,3,3)=78\). The pattern continues with increments \(24,32,40,\dots\), exactly as predicted by the linear recurrence.

From one cuboid to the global counting function

Once \(L(a,b,c,\ell)\) is known, the problem becomes a frequency table over all valid states. For each integer \(m\), count how many quadruples \((a,b,c,\ell)\) satisfy \(L(a,b,c,\ell)=m\). The first \(m\) with frequency 1000 is the desired answer.

No deeper number-theoretic transform is needed here. The decisive observation is that the true state space is already small enough once we exploit the order \(a \le b \le c\), the monotonic growth in \(c\) and \(\ell\), and the fact that only cuboids with first layer at most the current limit can matter.

How the Code Works

Count every layer size up to a temporary limit

The implementation keeps a temporary upper bound \(M\) and allocates an array of frequencies for \(0,1,\dots,M\). It then enumerates all ordered triples \(a \le b \le c\) whose first layer fits inside that bound, evaluates \(L(a,b,c,\ell)\) for \(\ell=1,2,3,\dots\), and increments the frequency of each layer size that does not exceed \(M\).

Use monotonicity to stop each nested loop early

The loops over \(a\), \(b\), and \(c\) stop as soon as the smallest possible first layer in that branch exceeds \(M\). Concretely, the outer loop needs only \(a\) such that \(L(a,a,a,1) \le M\); for fixed \(a\), the next loop needs only \(b\) such that \(L(a,b,b,1) \le M\); and for fixed \(a,b\), the loop over \(c\) stops when \(L(a,b,c,1) \gt M\). Inside a chosen cuboid, the layer loop stops as soon as \(L(a,b,c,\ell) \gt M\).

This structure matches the mathematics exactly: because the layer formula is increasing in every search variable, nothing beyond the first violation can ever come back below the limit.

Expand the search window until the target frequency appears

The search begins with a moderate limit and scans the completed frequency table from left to right. If no value has frequency 1000 yet, the limit is doubled and the whole count is repeated. Since the limits grow geometrically, the total work over all failed passes stays within a constant factor of the final successful pass.

One implementation also includes simple checkpoints: \(L(1,1,1,1)=6\), and the well-known sample count \(C(154)=10\). Those checks confirm both the layer formula and the counting logic before the full target of 1000 is attempted.

Complexity Analysis

For a successful final limit \(M\), the frequency table uses \(O(M)\) memory. The scan that looks for the first value with frequency 1000 also costs \(O(M)\).

The enumeration cost is best described in output-sensitive form. Let \(A(M)\) be the set of ordered cuboids with \(L(a,b,c,1) \le M\), and let \(r(a,b,c;M)\) be the largest layer index with \(L(a,b,c,\ell) \le M\). Then one counting pass costs

$$O\!\left(M+\sum_{(a,b,c)\in A(M)} r(a,b,c;M)\right).$$

Because \(L(a,b,c,\ell)\) is quadratic in \(\ell\), each \(r(a,b,c;M)\) is \(O(\sqrt{M})\) as a safe upper envelope. In practice the runtime is much better than that crude bound suggests, because the monotone breaks in \(b\), \(c\), and \(\ell\) prune the search aggressively. The repeated doubling of the limit changes only the constant factor, not the overall behavior.

Footnotes and References

  1. Problem page: Project Euler 126 - Cuboid Layers
  2. Cuboid geometry: Wikipedia - Cuboid
  3. Surface area: Wikipedia - Surface area
  4. Arithmetic progressions: Wikipedia - Arithmetic progression
  5. Quadratic functions: Wikipedia - Quadratic function

Problemzusammenfassung

Ein Quader mit Kantenlängen \(a\), \(b\) und \(c\) wird von aufeinanderfolgenden äußeren Schichten aus Einheitswürfeln umhüllt. Wenn die Dimensionen als \(a \le b \le c\) geordnet werden und der Schichtindex \(\ell \ge 1\) ist, bezeichne \(L(a,b,c,\ell)\) die Anzahl der Würfel in der \(\ell\)-ten Schicht. Gesucht ist die kleinste Zahl \(m\), für die genau 1000 geordnete Quader-Schicht-Kombinationen \(m\) Würfel liefern.

Praktisch schreibt man dafür die Zählfunktion

$$C(m)=\#\{(a,b,c,\ell): a \le b \le c,\ \ell \ge 1,\ L(a,b,c,\ell)=m\}.$$

Gesucht ist also das kleinste \(m\) mit \(C(m)=1000\). Die Ordnung \(a \le b \le c\) entfernt nur Drehungsduplikate; verschiedene Formen und verschiedene Schichtnummern werden weiterhin getrennt gezählt.

Mathematischer Ansatz

Die Implementierungen beruhen auf einer expliziten Formel für die Größe einer Quader-Schicht und auf einer vollständigen Zählung aller zulässigen Zustände \((a,b,c,\ell)\).

Die erste Schicht ist einfach die Oberfläche des Quaders

Die erste äußere Schicht bedeckt die sechs Seitenflächen des Quaders. Zwei Flächen haben Inhalt \(ab\), zwei haben \(ac\) und zwei haben \(bc\). Daher gilt

$$L(a,b,c,1)=2(ab+ac+bc).$$

Das ist die kleinste Schicht dieses Quaders. Bei einer Suche bis zur Grenze \(m\) kommen also nur Quader mit \(2(ab+ac+bc) \le m\) in Frage.

Wie eine Schicht in die nächste übergeht

Der Übergang von Schicht \(\ell\) zu Schicht \(\ell+1\) fügt einen ein Würfel dicken Rand um die bisherige Hülle hinzu. Der lineare Anteil stammt von den 12 Kantenbändern: Jede der drei Kantenlängen tritt viermal auf, also ist der Kantenbeitrag immer \(4(a+b+c)\).

Dazu kommt ein Eckbeitrag. Sobald die Hülle dicker als die erste Schicht ist, verbreitern sich die gestuften Bereiche an den acht Ecken weiter, und zusammen liefern sie beim Übergang von \(\ell\) nach \(\ell+1\) genau \(8(\ell-1)\) neue Würfel. Damit gilt

$$L(a,b,c,\ell+1)-L(a,b,c,\ell)=4(a+b+c)+8(\ell-1)=4(a+b+c+2\ell-2).$$

Für einen festen Quader bilden die aufeinanderfolgenden Differenzen also eine arithmetische Folge. Genau deshalb kann die innere Schleife über die Schichten sofort abbrechen, sobald die aktuelle Grenze überschritten wird.

Durch Aufsummieren entsteht die geschlossene Formel

Ausgehend von \(L(a,b,c,1)=2(ab+ac+bc)\) und nach \(\ell-1\) Summationsschritten erhält man

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c)+4(\ell-1)(\ell-2).$$

Die letzten beiden Terme kann man auch zusammenfassen zu

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c+\ell-2).$$

Genau diese Formel verwenden die C++-, Python- und Java-Implementierungen. Daraus folgen sofort einige Invarianten: \(L(a,b,c,\ell)\) ist immer gerade, für feste \(a,b,c\) streng wachsend in \(\ell\), und auch in jeder Dimension einzeln wachsend, wenn die anderen festgehalten werden.

Durchgerechnetes Beispiel: der \(1 \times 2 \times 3\)-Quader

Für \(a=1\), \(b=2\), \(c=3\) enthält die erste Schicht

$$L(1,2,3,1)=2(1\cdot2+1\cdot3+2\cdot3)=2(11)=22.$$

Die Rekursion liefert dann

$$L(1,2,3,2)-L(1,2,3,1)=4(1+2+3)=24,$$

also \(L(1,2,3,2)=46\). Für die nächste Schicht ist der Zuwachs um 8 größer:

$$L(1,2,3,3)-L(1,2,3,2)=4(1+2+3+2)=32,$$

also \(L(1,2,3,3)=78\). Die Zuwächse \(24,32,40,\dots\) zeigen genau die lineare Struktur der Differenzen.

Von einem Quader zur globalen Zählfunktion

Sobald \(L(a,b,c,\ell)\) feststeht, ist das Problem nur noch eine Häufigkeitstabelle über alle zulässigen Zustände. Für jede Zahl \(m\) zählt man, wie viele Vierer \((a,b,c,\ell)\) die Gleichung \(L(a,b,c,\ell)=m\) erfüllen. Das erste \(m\) mit Häufigkeit 1000 ist die gesuchte Antwort.

Eine raffiniertere zahlentheoretische Transformation ist hier nicht nötig. Entscheidend ist, dass der tatsächliche Zustandsraum mit \(a \le b \le c\), monotonem Wachstum in \(c\) und \(\ell\) sowie der notwendigen Bedingung \(L(a,b,c,1) \le m\) bereits klein genug wird.

Wie der Code arbeitet

Alle Schichtgrößen bis zu einer vorläufigen Grenze zählen

Die Implementierung verwendet eine vorläufige Obergrenze \(M\) und ein Frequenzarray für \(0,1,\dots,M\). Dann werden alle geordneten Tripel \(a \le b \le c\) durchlaufen, deren erste Schicht in diese Grenze passt, und für jedes solche Tripel werden die Werte \(L(a,b,c,\ell)\) für \(\ell=1,2,3,\dots\) berechnet. Jede Schichtgröße, die \(M\) nicht überschreitet, erhöht ihren Zähler um eins.

Monotonie beendet jede Schleife so früh wie möglich

Die Schleifen über \(a\), \(b\) und \(c\) brechen jeweils an der ersten Stelle ab, an der selbst die kleinste mögliche erste Schicht in diesem Zweig größer als \(M\) ist. Konkret braucht die äußere Schleife nur \(a\) mit \(L(a,a,a,1) \le M\); für festes \(a\) braucht die nächste Schleife nur \(b\) mit \(L(a,b,b,1) \le M\); und für feste \(a,b\) endet die Schleife über \(c\), sobald \(L(a,b,c,1) \gt M\) gilt. Innerhalb eines gewählten Quaders endet die Schichtschleife, sobald \(L(a,b,c,\ell) \gt M\) wird.

Das ist exakt die mathematische Monotonie der Formel: Nach der ersten Überschreitung kann in derselben Richtung kein späterer Wert wieder unter die Grenze fallen.

Das Suchfenster vergrößern, bis die Zielhäufigkeit auftaucht

Die Suche beginnt mit einer moderaten Grenze und durchsucht danach die fertige Häufigkeitstabelle von links nach rechts. Falls noch kein Wert die Häufigkeit 1000 besitzt, wird die Grenze verdoppelt und die gesamte Zählung wiederholt. Weil die Grenzen geometrisch wachsen, kostet die Summe aller erfolglosen Durchläufe nur einen konstanten Faktor mehr als der letzte erfolgreiche Durchlauf.

Eine der Implementierungen prüft zusätzlich zwei einfache Kontrollpunkte: \(L(1,1,1,1)=6\) und das bekannte Beispiel \(C(154)=10\). Damit werden Formel und Zähllogik vor der eigentlichen Zielsuche abgesichert.

Komplexitätsanalyse

Für eine erfolgreiche Endgrenze \(M\) benötigt das Frequenzarray \(O(M)\) Speicher. Auch der abschließende lineare Durchlauf, der den ersten Wert mit Häufigkeit 1000 sucht, kostet \(O(M)\).

Die Enumerationskosten beschreibt man am saubersten ausgabesensitiv. Sei \(A(M)\) die Menge aller geordneten Quader mit \(L(a,b,c,1) \le M\), und sei \(r(a,b,c;M)\) der größte Schichtindex mit \(L(a,b,c,\ell) \le M\). Dann kostet ein Zähldurchlauf

$$O\!\left(M+\sum_{(a,b,c)\in A(M)} r(a,b,c;M)\right).$$

Da \(L(a,b,c,\ell)\) quadratisch in \(\ell\) ist, gilt als sichere obere Hülle \(r(a,b,c;M)=O(\sqrt{M})\). In der Praxis ist die Laufzeit deutlich besser, weil die monotonen Abbruchbedingungen in \(b\), \(c\) und \(\ell\) sehr stark kürzen. Das wiederholte Verdoppeln der Grenze ändert nur den konstanten Faktor, nicht das asymptotische Verhalten.

Fußnoten und Quellen

  1. Problemseite: Project Euler 126 - Cuboid Layers
  2. Geometrie des Quaders: Wikipedia - Cuboid
  3. Oberflächeninhalt: Wikipedia - Surface area
  4. Arithmetische Folge: Wikipedia - Arithmetic progression
  5. Quadratische Funktion: Wikipedia - Quadratic function

Problem Özeti

Kenar uzunlukları \(a\), \(b\) ve \(c\) olan bir dikdörtgenler prizmasının etrafına art arda birim küp katmanları örülür. Boyutları \(a \le b \le c\) olacak şekilde sıralayıp katman numarasını \(\ell \ge 1\) ile gösterelim. Bu durumda \(L(a,b,c,\ell)\), \(\ell\)-inci katmandaki küp sayısıdır. Sorulan şey, tam olarak 1000 farklı prizma-katman durumunun ürettiği en küçük \(m\) değeridir.

Bunu sayma fonksiyonu olarak

$$C(m)=\#\{(a,b,c,\ell): a \le b \le c,\ \ell \ge 1,\ L(a,b,c,\ell)=m\}$$

şeklinde yazabiliriz. Aranan sonuç, \(C(m)=1000\) eşitliğini sağlayan en küçük \(m\)'dir. Buradaki sıralama yalnızca döndürmeyle oluşan tekrarları kaldırır; farklı biçimler ve farklı katman numaraları ayrı ayrı sayılır.

Matematiksel Yaklaşım

Çözümün omurgası, bir prizmanın \(\ell\)-inci katmanını veren kapalı formül ile tüm geçerli \((a,b,c,\ell)\) durumlarını sayan doğrudan enumerasyondur.

İlk katman prizmanın yüzeyidir

İlk dış katman, prizmanın altı yüzünü kaplar. İki yüzün alanı \(ab\), iki yüzün alanı \(ac\), iki yüzün alanı da \(bc\) olduğundan

$$L(a,b,c,1)=2(ab+ac+bc)$$

elde edilir. Bu, o prizma için mümkün olan en küçük katmandır. Dolayısıyla üst sınırı \(m\) olan bir aramada yalnızca \(2(ab+ac+bc) \le m\) koşulunu sağlayan prizmalar dikkate alınır.

Bir katmandan sonrakine geçiş

\(\ell\). katmandan \(\ell+1\). katmana geçerken mevcut kabuğun etrafına kalınlığı bir küp olan yeni bir çerçeve eklenir. Doğrusal kısım 12 kenar şeridinden gelir: üç kenar uzunluğunun her biri dört kez göründüğü için kenar katkısı her zaman \(4(a+b+c)\)'dir.

Buna bir de köşe katkısı eklenir. Kabuk ilk katmanın ötesine geçtikten sonra sekiz köşedeki basamaklı bölgeler genişlemeye devam eder ve \(\ell\)'den \(\ell+1\)'e geçişte toplam \(8(\ell-1)\) yeni küp getirir. Böylece

$$L(a,b,c,\ell+1)-L(a,b,c,\ell)=4(a+b+c)+8(\ell-1)=4(a+b+c+2\ell-2)$$

olur. Sabit bir prizma için artışlar aritmetik dizi oluşturur. Kodun katman döngüsünü erken bitirebilmesi tam olarak bu monoton yapı sayesinde mümkündür.

Özyinelemeyi toplayınca kapalı formül çıkar

\(L(a,b,c,1)=2(ab+ac+bc)\) başlangıcından gelip yukarıdaki farkı \(\ell-1\) kez toplarsak

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c)+4(\ell-1)(\ell-2)$$

elde edilir. Son iki terim birleştirilirse

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c+\ell-2)$$

şeklindeki daha sık kullanılan yazım ortaya çıkar. C++, Python ve Java uygulamalarının kullandığı formül tam olarak budur. Bu formülden birkaç temel özellik hemen görülür: \(L(a,b,c,\ell)\) her zaman çifttir, sabit \(a,b,c\) için \(\ell\) arttıkça sıkı biçimde büyür ve diğerleri sabitken \(a\), \(b\), \(c\) değişkenlerinin her birinde de artandır.

Çalışılmış örnek: \(1 \times 2 \times 3\) prizması

\(a=1\), \(b=2\), \(c=3\) için ilk katman

$$L(1,2,3,1)=2(1\cdot2+1\cdot3+2\cdot3)=2(11)=22$$

küp içerir. Özyineleme bunu

$$L(1,2,3,2)-L(1,2,3,1)=4(1+2+3)=24$$

şeklinde büyütür; dolayısıyla \(L(1,2,3,2)=46\). Bir sonraki artış 8 daha büyüktür:

$$L(1,2,3,3)-L(1,2,3,2)=4(1+2+3+2)=32,$$

buradan da \(L(1,2,3,3)=78\) gelir. Artışların \(24,32,40,\dots\) biçiminde gitmesi, farkların doğrusal olduğunu açıkça gösterir.

Tek bir prizma formülünden küresel sayma problemine geçiş

\(L(a,b,c,\ell)\) formülü elde edilince geriye yalnızca frekans tablosu kurmak kalır. Her \(m\) değeri için, \(L(a,b,c,\ell)=m\) eşitliğini sağlayan kaç farklı \((a,b,c,\ell)\) olduğu sayılır. Frekansı 1000 olan ilk \(m\), sorunun cevabıdır.

Burada daha karmaşık bir sayı teorisi aracına ihtiyaç yoktur. Asıl önemli nokta, \(a \le b \le c\) düzeni, \(c\) ve \(\ell\) yönündeki monoton büyüme ve \(L(a,b,c,1) \le m\) zorunlu koşulu sayesinde gerçek durum uzayının zaten yeterince küçük olmasıdır.

Kod Nasıl Çalışır

Geçici bir üst sınıra kadar tüm katman boyutlarını sayma

Uygulama önce geçici bir üst sınır \(M\) seçer ve \(0,1,\dots,M\) için bir frekans dizisi ayırır. Sonra ilk katmanı bu sınıra sığan tüm sıralı \(a \le b \le c\) üçlülerini gezer, her prizma için \(L(a,b,c,\ell)\) değerlerini \(\ell=1,2,3,\dots\) için üretir ve \(M\)'yi aşmayan her katman boyutunun sayacını bir artırır.

Monotonluk sayesinde iç içe döngüler erkenden durur

\(a\), \(b\) ve \(c\) döngüleri, ilgili dalda mümkün olan en küçük ilk katman bile \(M\)'yi aştığında hemen sona erer. Daha açık söylersek dış döngü yalnızca \(L(a,a,a,1) \le M\) olan \(a\) değerlerini gezer; sabit \(a\) için ikinci döngü yalnızca \(L(a,b,b,1) \le M\) olan \(b\) değerlerini gezer; sabit \(a,b\) için de \(L(a,b,c,1) \gt M\) olduğu anda \(c\) döngüsü biter. Seçilmiş bir prizma içinde katman döngüsü ise \(L(a,b,c,\ell) \gt M\) olur olmaz sonlanır.

Bu, formülün matematiksel monotonluğunun doğrudan kod karşılığıdır: ilk ihlalden sonra aynı yönde gidildiğinde tekrar sınırın altına dönmek mümkün değildir.

Hedef frekans görünene kadar arama penceresini büyütme

Arama orta büyüklükte bir sınırla başlar ve üretilen frekans tablosu soldan sağa taranır. Henüz frekansı 1000 olan bir değer yoksa sınır iki katına çıkarılır ve bütün sayım baştan yapılır. Sınırlar geometrik olarak büyüdüğü için başarısız denemelerin toplam maliyeti son başarılı geçişin yalnızca sabit katı kadar olur.

Uygulamalardan biri ayrıca iki küçük kontrol de yapar: \(L(1,1,1,1)=6\) ve bilinen örnek \(C(154)=10\). Böylece tam hedef olan 1000'e geçmeden önce hem katman formülü hem de sayma mantığı doğrulanmış olur.

Karmaşıklık Analizi

Başarılı son sınır \(M\) için frekans dizisi \(O(M)\) bellek kullanır. Frekansı 1000 olan ilk değeri arayan son tarama da \(O(M)\) zaman alır.

Enumerasyon maliyetini en temiz biçimde çıktı-duyarlı olarak yazabiliriz. \(A(M)\), \(L(a,b,c,1) \le M\) koşulunu sağlayan sıralı prizmalar kümesi olsun; \(r(a,b,c;M)\) de \(L(a,b,c,\ell) \le M\) eşitsizliğini sağlayan en büyük katman indeksi olsun. O zaman tek bir sayım geçişinin maliyeti

$$O\!\left(M+\sum_{(a,b,c)\in A(M)} r(a,b,c;M)\right)$$

şeklindedir. \(L(a,b,c,\ell)\), \(\ell\)'de ikinci dereceden olduğundan güvenli bir üst kabuk olarak \(r(a,b,c;M)=O(\sqrt{M})\) denebilir. Pratikte gerçek çalışma süresi bundan çok daha iyidir; çünkü \(b\), \(c\) ve \(\ell\) yönlerindeki monoton kesmeler aramayı ciddi biçimde budar. Sınırı tekrar tekrar iki katına çıkarmak da yalnızca sabit katsayıyı değiştirir.

Dipnotlar ve Kaynaklar

  1. Problem sayfası: Project Euler 126 - Cuboid Layers
  2. Dikdörtgenler prizması: Wikipedia - Cuboid
  3. Yüzey alanı: Wikipedia - Surface area
  4. Aritmetik dizi: Wikipedia - Arithmetic progression
  5. İkinci dereceden fonksiyon: Wikipedia - Quadratic function

Resumen del Problema

Un ortoedro de aristas \(a\), \(b\) y \(c\) queda rodeado por capas sucesivas de cubos unitarios. Si ordenamos las dimensiones como \(a \le b \le c\) y llamamos \(\ell \ge 1\) al número de capa, \(L(a,b,c,\ell)\) denota la cantidad de cubos de la capa \(\ell\). El problema pide el menor entero \(m\) para el cual exactamente 1000 combinaciones ordenadas de ortoedro y capa producen \(m\) cubos.

Es útil escribir la función de conteo como

$$C(m)=\#\{(a,b,c,\ell): a \le b \le c,\ \ell \ge 1,\ L(a,b,c,\ell)=m\}.$$

Por tanto, hay que hallar el menor \(m\) tal que \(C(m)=1000\). La condición \(a \le b \le c\) elimina duplicados por rotación, pero formas distintas y números de capa distintos siguen contando por separado.

Enfoque Matemático

La solución se apoya en una fórmula cerrada para el tamaño de la capa \(\ell\) y en una enumeración completa de todos los estados válidos \((a,b,c,\ell)\).

La primera capa coincide con la superficie del ortoedro

La primera capa exterior cubre las seis caras. Dos caras tienen área \(ab\), dos tienen área \(ac\) y dos tienen área \(bc\), de modo que

$$L(a,b,c,1)=2(ab+ac+bc).$$

Ese es el tamaño mínimo de capa para ese ortoedro. En una búsqueda acotada por \(m\), solo importan por tanto los triples con \(2(ab+ac+bc) \le m\).

Cómo crece una capa al pasar a la siguiente

Al pasar de la capa \(\ell\) a la capa \(\ell+1\), se añade un borde de grosor uno alrededor de la envoltura anterior. La parte lineal procede de las 12 bandas de arista: cada una de las tres longitudes aparece cuatro veces, así que la contribución de las aristas es siempre \(4(a+b+c)\).

También hay una contribución de las esquinas. Una vez superada la primera capa, las regiones escalonadas alrededor de las ocho esquinas siguen ensanchándose, y en conjunto aportan \(8(\ell-1)\) cubos nuevos cuando el índice sube de \(\ell\) a \(\ell+1\). Por eso

$$L(a,b,c,\ell+1)-L(a,b,c,\ell)=4(a+b+c)+8(\ell-1)=4(a+b+c+2\ell-2).$$

Para un ortoedro fijo, las diferencias sucesivas forman una progresión aritmética. Esa monotonía es exactamente lo que permite cortar el bucle interno de capas en cuanto se supera el límite actual.

La suma de la recurrencia da la fórmula cerrada

Partiendo de \(L(a,b,c,1)=2(ab+ac+bc)\) y sumando el incremento anterior durante \(\ell-1\) pasos se obtiene

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c)+4(\ell-1)(\ell-2).$$

Los dos últimos términos suelen reagruparse como

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c+\ell-2).$$

Esa es exactamente la fórmula usada por las implementaciones en C++, Python y Java. Se deducen enseguida varias propiedades: \(L(a,b,c,\ell)\) siempre es par, es estrictamente creciente en \(\ell\) para \(a,b,c\) fijos, y también crece con cada una de las dimensiones si las otras se mantienen constantes.

Ejemplo trabajado: el ortoedro \(1 \times 2 \times 3\)

Para \(a=1\), \(b=2\), \(c=3\), la primera capa contiene

$$L(1,2,3,1)=2(1\cdot2+1\cdot3+2\cdot3)=2(11)=22.$$

La recurrencia dice entonces

$$L(1,2,3,2)-L(1,2,3,1)=4(1+2+3)=24,$$

de donde \(L(1,2,3,2)=46\). Para la capa siguiente el incremento crece en 8:

$$L(1,2,3,3)-L(1,2,3,2)=4(1+2+3+2)=32,$$

y por tanto \(L(1,2,3,3)=78\). Los incrementos \(24,32,40,\dots\) muestran con claridad la estructura lineal de la recurrencia.

Del caso individual a la función global \(C(m)\)

Una vez conocida la fórmula \(L(a,b,c,\ell)\), el resto del problema consiste en construir una tabla de frecuencias. Para cada valor \(m\), contamos cuántos cuádruplos \((a,b,c,\ell)\) satisfacen \(L(a,b,c,\ell)=m\). El primer \(m\) cuya frecuencia es 1000 es la respuesta buscada.

No hace falta una transformación teórica más sofisticada. Lo decisivo es que el espacio real de estados ya es manejable si se aprovechan el orden \(a \le b \le c\), el crecimiento monótono en \(c\) y en \(\ell\), y la condición necesaria \(L(a,b,c,1) \le m\).

Cómo funciona el código

Contar todos los tamaños de capa hasta un límite provisional

La implementación fija un límite provisional \(M\) y reserva un arreglo de frecuencias para \(0,1,\dots,M\). Después enumera todos los triples ordenados \(a \le b \le c\) cuya primera capa cabe en ese límite, calcula \(L(a,b,c,\ell)\) para \(\ell=1,2,3,\dots\) y aumenta en uno el contador correspondiente a cada tamaño de capa que no supere \(M\).

La monotonía permite cortar pronto cada bucle anidado

Los bucles sobre \(a\), \(b\) y \(c\) se detienen en cuanto la menor primera capa posible de esa rama ya supera \(M\). En concreto, el bucle exterior solo necesita valores de \(a\) con \(L(a,a,a,1) \le M\); para un \(a\) fijo, el siguiente bucle solo necesita \(b\) con \(L(a,b,b,1) \le M\); y para \(a,b\) fijos, el bucle sobre \(c\) termina cuando \(L(a,b,c,1) \gt M\). Dentro de un ortoedro dado, el bucle de capas termina en el primer \(\ell\) para el que \(L(a,b,c,\ell) \gt M\).

Esa estructura es la traducción directa de la monotonía matemática de la fórmula: después de la primera violación, ningún valor posterior del mismo ramal puede volver a caer por debajo del límite.

Ampliar la ventana de búsqueda hasta encontrar la frecuencia objetivo

La búsqueda arranca con un límite moderado y luego recorre la tabla de frecuencias de izquierda a derecha. Si todavía no aparece ningún valor con frecuencia 1000, el límite se duplica y el conteo completo se repite. Como los límites crecen geométricamente, el coste total de las pasadas fallidas solo añade un factor constante respecto de la pasada final exitosa.

Una de las implementaciones también verifica dos controles sencillos: \(L(1,1,1,1)=6\) y el ejemplo conocido \(C(154)=10\). Eso valida tanto la fórmula de capas como la lógica de conteo antes de atacar el objetivo final de 1000.

Análisis de Complejidad

Para un límite final exitoso \(M\), el arreglo de frecuencias usa \(O(M)\) memoria. El barrido final que busca el primer valor con frecuencia 1000 también cuesta \(O(M)\).

El coste de enumeración se describe mejor en forma sensible a la salida. Sea \(A(M)\) el conjunto de ortoedros ordenados con \(L(a,b,c,1) \le M\), y sea \(r(a,b,c;M)\) el mayor índice de capa que satisface \(L(a,b,c,\ell) \le M\). Entonces una pasada de conteo cuesta

$$O\!\left(M+\sum_{(a,b,c)\in A(M)} r(a,b,c;M)\right).$$

Como \(L(a,b,c,\ell)\) es cuadrática en \(\ell\), cada \(r(a,b,c;M)\) está acotado por \(O(\sqrt{M})\) de forma segura. En la práctica el tiempo real es bastante menor, porque las condiciones de corte monótono en \(b\), \(c\) y \(\ell\) podan la búsqueda con mucha eficacia. El proceso de duplicar el límite solo modifica la constante multiplicativa.

Notas y referencias

  1. Página del problema: Project Euler 126 - Cuboid Layers
  2. Geometría del ortoedro: Wikipedia - Cuboid
  3. Área de superficie: Wikipedia - Surface area
  4. Progresión aritmética: Wikipedia - Arithmetic progression
  5. Función cuadrática: Wikipedia - Quadratic function

问题概述

设一个长方体的三条边长为 \(a\)、\(b\)、\(c\),在它外面一层一层包裹单位立方体。若把尺寸按 \(a \le b \le c\) 排序,并把层号记为 \(\ell \ge 1\),则 \(L(a,b,c,\ell)\) 表示第 \(\ell\) 层所需的立方体数。题目要求的是:恰好有 1000 个有序长方体-层数组合产生同一个数值时,这个数值 \(m\) 的最小值是多少。

把计数函数写成

$$C(m)=\#\{(a,b,c,\ell): a \le b \le c,\ \ell \ge 1,\ L(a,b,c,\ell)=m\}.$$

那么目标就是求最小的 \(m\),使得 \(C(m)=1000\)。其中 \(a \le b \le c\) 只是为了消除旋转带来的重复;不同的长方体形状以及不同的层号仍然都要分别计数。

数学方法

这道题的核心并不是复杂变换,而是先写出长方体第 \(\ell\) 层的精确公式,再把所有满足条件的 \((a,b,c,\ell)\) 状态完整枚举出来。

第一层就是长方体的表面

最内侧的外包层直接贴在原长方体的六个面上。面积为 \(ab\) 的面有两个,面积为 \(ac\) 的面有两个,面积为 \(bc\) 的面也有两个,因此

$$L(a,b,c,1)=2(ab+ac+bc).$$

这就是该长方体可能出现的最小层大小。因此如果当前只考虑不超过 \(m\) 的层大小,那么只有满足 \(2(ab+ac+bc) \le m\) 的长方体才可能贡献答案。

从一层长到下一层的递推

从第 \(\ell\) 层扩展到第 \(\ell+1\) 层,相当于在现有外壳周围再加一圈厚度为 1 的立方体。线性部分来自 12 条棱上的条带:三种棱长各出现 4 次,所以棱带贡献恒为 \(4(a+b+c)\)。

除此之外还有角上的贡献。超过第一层以后,八个角附近的阶梯形区域会继续向外展开;当层号从 \(\ell\) 增加到 \(\ell+1\) 时,这部分总共再增加 \(8(\ell-1)\) 个立方体。因此有

$$L(a,b,c,\ell+1)-L(a,b,c,\ell)=4(a+b+c)+8(\ell-1)=4(a+b+c+2\ell-2).$$

也就是说,对固定长方体来说,相邻两层之间的增量本身构成等差数列。代码里按层递增并在超过上界时立即停止,正是利用了这种单调性。

把递推求和得到闭式公式

由初值 \(L(a,b,c,1)=2(ab+ac+bc)\) 出发,把上面的增量累加 \(\ell-1\) 次,就得到

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c)+4(\ell-1)(\ell-2).$$

后两项也常合并写成

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c+\ell-2).$$

这正是 C++、Python 和 Java 实现中使用的公式。由它可以立刻看出几个重要性质:\(L(a,b,c,\ell)\) 永远是偶数;对固定 \(a,b,c\) 而言,\(L\) 随 \(\ell\) 严格递增;而在其余变量固定时,它对 \(a\)、\(b\)、\(c\) 也都是递增的。

具体例子:\(1 \times 2 \times 3\) 长方体

令 \(a=1\)、\(b=2\)、\(c=3\),则第一层大小为

$$L(1,2,3,1)=2(1\cdot2+1\cdot3+2\cdot3)=2(11)=22.$$

递推式告诉我们

$$L(1,2,3,2)-L(1,2,3,1)=4(1+2+3)=24,$$

所以 \(L(1,2,3,2)=46\)。下一层的增量又多 8:

$$L(1,2,3,3)-L(1,2,3,2)=4(1+2+3+2)=32,$$

因此 \(L(1,2,3,3)=78\)。增量序列 \(24,32,40,\dots\) 清楚地体现了“层数越外,新增越多”的线性规律。

从单个长方体公式到整体计数函数

一旦 \(L(a,b,c,\ell)\) 明确下来,题目就变成了一个频数统计问题。对每个整数 \(m\),统计有多少个四元组 \((a,b,c,\ell)\) 满足 \(L(a,b,c,\ell)=m\)。第一个出现频数 1000 的 \(m\) 就是答案。

这里不需要 Möbius 反演、生成函数或别的重型工具。真正关键的是:按 \(a \le b \le c\) 去重后,配合 \(c\) 与 \(\ell\) 方向上的单调增长,以及必要条件 \(L(a,b,c,1) \le m\),真实搜索空间已经足够小,可以直接精确枚举。

代码如何工作

先在一个临时上界内统计所有层大小

实现先选定一个临时上界 \(M\),并建立一个从 0 到 \(M\) 的频数数组。随后枚举所有第一层不超过 \(M\) 的有序三元组 \(a \le b \le c\),对每个长方体依次计算 \(\ell=1,2,3,\dots\) 时的 \(L(a,b,c,\ell)\),只要结果不超过 \(M\),就把对应位置的频数加 1。

利用单调性提前终止每一层循环

\(a\)、\(b\)、\(c\) 三层循环都能在最早的位置停止,因为一旦该分支的最小可能第一层已经超过 \(M\),后面更不可能回到界内。具体来说,最外层只需要满足 \(L(a,a,a,1) \le M\) 的 \(a\);固定 \(a\) 后,只需要满足 \(L(a,b,b,1) \le M\) 的 \(b\);固定 \(a,b\) 后,当 \(L(a,b,c,1) \gt M\) 时,\(c\) 循环即可结束。进入某个具体长方体后,层号 \(\ell\) 的循环则在 \(L(a,b,c,\ell) \gt M\) 时停止。

这一点与数学公式完全一致:由于公式对每个搜索变量都单调递增,同一方向上第一次越界之后,就不可能再重新落回上界以内。

不断扩大搜索窗口,直到出现目标频数

搜索从一个适中的上界开始,然后从小到大扫描整张频数表。如果还没有任何数值的频数达到 1000,就把上界翻倍,再重新统计一次。因为上界按几何级数增长,所有失败轮次加起来的代价只比最终成功轮次多一个常数因子。

其中一个实现还加入了两个简单校验:\(L(1,1,1,1)=6\),以及已知样例 \(C(154)=10\)。这能在正式求解 1000 之前确认层公式和计数逻辑都没有偏差。

复杂度分析

设最终成功的上界为 \(M\)。频数数组占用 \(O(M)\) 空间,而最后从左到右寻找第一个频数为 1000 的值也需要 \(O(M)\) 时间。

枚举部分更适合写成输出敏感形式。记 \(A(M)\) 为所有满足 \(L(a,b,c,1) \le M\) 的有序长方体集合,记 \(r(a,b,c;M)\) 为满足 \(L(a,b,c,\ell) \le M\) 的最大层号。那么一次完整统计的时间复杂度是

$$O\!\left(M+\sum_{(a,b,c)\in A(M)} r(a,b,c;M)\right).$$

由于 \(L(a,b,c,\ell)\) 关于 \(\ell\) 是二次函数,所以 \(r(a,b,c;M)\) 可以安全地用 \(O(\sqrt{M})\) 作为上界。实际运行通常远好于这个粗略估计,因为 \(b\)、\(c\) 与 \(\ell\) 的单调剪枝非常有效。反复把上界翻倍只会影响常数,不改变整体增长级别。

脚注与参考资料

  1. 题目页面:Project Euler 126 - Cuboid Layers
  2. 长方体几何:Wikipedia - Cuboid
  3. 表面积:Wikipedia - Surface area
  4. 等差数列:Wikipedia - Arithmetic progression
  5. 二次函数:Wikipedia - Quadratic function

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

Пусть прямоугольный параллелепипед имеет ребра \(a\), \(b\) и \(c\). Вокруг него последовательно строятся внешние слои из единичных кубов. Если упорядочить размеры как \(a \le b \le c\) и обозначить номер слоя через \(\ell \ge 1\), то \(L(a,b,c,\ell)\) означает число кубов в \(\ell\)-м слое. Нужно найти наименьшее число \(m\), которое получается ровно у 1000 упорядоченных комбинаций «параллелепипед плюс номер слоя».

Удобно записать функцию подсчета так:

$$C(m)=\#\{(a,b,c,\ell): a \le b \le c,\ \ell \ge 1,\ L(a,b,c,\ell)=m\}.$$

Тогда требуется минимальное \(m\), для которого \(C(m)=1000\). Условие \(a \le b \le c\) лишь устраняет повороты одной и той же фигуры; разные формы и разные номера слоев по-прежнему считаются отдельно.

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

Решение опирается на точную формулу для размера \(\ell\)-го слоя и на полный перебор всех допустимых состояний \((a,b,c,\ell)\).

Первый слой равен поверхности параллелепипеда

Самый внутренний внешний слой покрывает шесть граней исходного тела. Две грани имеют площадь \(ab\), две имеют площадь \(ac\), и еще две имеют площадь \(bc\). Поэтому

$$L(a,b,c,1)=2(ab+ac+bc).$$

Это минимальный размер слоя для данного параллелепипеда. Значит, при поиске до границы \(m\) имеют смысл только те тройки, для которых \(2(ab+ac+bc) \le m\).

Как один слой переходит в следующий

Переход от слоя \(\ell\) к слою \(\ell+1\) означает добавление рамки толщины 1 вокруг уже существующей оболочки. Линейная часть приходит от 12 полос вдоль ребер: каждая из трех длин ребра встречается по четыре раза, так что вклад ребер всегда равен \(4(a+b+c)\).

Есть и вклад углов. После первой оболочки ступенчатые области у восьми углов продолжают расширяться, и при переходе от \(\ell\) к \(\ell+1\) вместе дают еще \(8(\ell-1)\) кубов. Поэтому

$$L(a,b,c,\ell+1)-L(a,b,c,\ell)=4(a+b+c)+8(\ell-1)=4(a+b+c+2\ell-2).$$

Для фиксированного параллелепипеда последовательные приращения образуют арифметическую прогрессию. Именно эта монотонность позволяет внутреннему циклу по слоям остановиться сразу после выхода за текущий предел.

Суммирование рекуррентного соотношения дает замкнутую формулу

Начиная с \(L(a,b,c,1)=2(ab+ac+bc)\) и суммируя указанный прирост \(\ell-1\) раз, получаем

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c)+4(\ell-1)(\ell-2).$$

Последние два слагаемых часто объединяют в запись

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c+\ell-2).$$

Именно эту формулу используют реализации на C++, Python и Java. Из нее сразу следуют полезные свойства: \(L(a,b,c,\ell)\) всегда четно, при фиксированных \(a,b,c\) строго растет по \(\ell\), и также возрастает по каждой из переменных \(a\), \(b\), \(c\), если остальные зафиксированы.

Разобранный пример: параллелепипед \(1 \times 2 \times 3\)

При \(a=1\), \(b=2\), \(c=3\) первый слой содержит

$$L(1,2,3,1)=2(1\cdot2+1\cdot3+2\cdot3)=2(11)=22.$$

Рекурсия далее дает

$$L(1,2,3,2)-L(1,2,3,1)=4(1+2+3)=24,$$

значит, \(L(1,2,3,2)=46\). Для следующего слоя прирост увеличивается еще на 8:

$$L(1,2,3,3)-L(1,2,3,2)=4(1+2+3+2)=32,$$

поэтому \(L(1,2,3,3)=78\). Приращения \(24,32,40,\dots\) наглядно показывают линейный рост разностей.

От формулы для одного тела к глобальной функции \(C(m)\)

Когда формула \(L(a,b,c,\ell)\) известна, задача сводится к построению таблицы частот. Для каждого числа \(m\) нужно посчитать, сколько четверок \((a,b,c,\ell)\) удовлетворяют равенству \(L(a,b,c,\ell)=m\). Первое \(m\) с частотой 1000 и есть искомый ответ.

Здесь не требуется никакой сложной арифметической трансформации. Главное наблюдение состоит в том, что после учета порядка \(a \le b \le c\), монотонного роста по \(c\) и \(\ell\), а также необходимого условия \(L(a,b,c,1) \le m\), реальное пространство состояний уже достаточно мало для прямого точного перебора.

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

Подсчет всех размеров слоев до временной границы

Реализация выбирает временный предел \(M\) и создает массив частот для значений \(0,1,\dots,M\). Затем перебираются все упорядоченные тройки \(a \le b \le c\), у которых первый слой не превосходит \(M\), после чего для каждой такой тройки вычисляются значения \(L(a,b,c,\ell)\) при \(\ell=1,2,3,\dots\). Каждый размер слоя, не превышающий \(M\), увеличивает свой счетчик на единицу.

Монотонность завершает каждый вложенный цикл как можно раньше

Циклы по \(a\), \(b\) и \(c\) обрываются в тот момент, когда даже минимально возможный первый слой в этой ветви уже больше \(M\). Иначе говоря, внешний цикл берет только те \(a\), для которых \(L(a,a,a,1) \le M\); при фиксированном \(a\) следующий цикл берет только те \(b\), для которых \(L(a,b,b,1) \le M\); а при фиксированных \(a,b\) цикл по \(c\) заканчивается, как только \(L(a,b,c,1) \gt M\). Для конкретного параллелепипеда цикл по \(\ell\) прекращается, когда \(L(a,b,c,\ell) \gt M\).

Это прямое отражение математической монотонности формулы: после первого выхода за предел никакой следующий элемент той же ветви уже не вернется внутрь диапазона.

Расширение окна поиска до появления нужной частоты

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

В одной из реализаций есть и две простые проверки: \(L(1,1,1,1)=6\) и известный пример \(C(154)=10\). Они подтверждают корректность формулы слоев и логики подсчета перед полным запуском с целью 1000.

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

Для успешной конечной границы \(M\) массив частот требует \(O(M)\) памяти. Финальный линейный проход, который ищет первое значение с частотой 1000, тоже занимает \(O(M)\) времени.

Стоимость перебора удобнее описывать в чувствительной к выходу форме. Пусть \(A(M)\) — множество всех упорядоченных параллелепипедов, для которых \(L(a,b,c,1) \le M\), а \(r(a,b,c;M)\) — наибольший номер слоя, удовлетворяющий неравенству \(L(a,b,c,\ell) \le M\). Тогда один проход подсчета стоит

$$O\!\left(M+\sum_{(a,b,c)\in A(M)} r(a,b,c;M)\right).$$

Поскольку \(L(a,b,c,\ell)\) квадратична по \(\ell\), для каждого такого \(r(a,b,c;M)\) безопасно писать верхнюю оценку \(O(\sqrt{M})\). На практике время существенно лучше этой грубой оценки, потому что монотонные остановки по \(b\), \(c\) и \(\ell\) очень сильно сокращают поиск. Повторное удвоение границы меняет лишь константу.

Сноски и источники

  1. Страница задачи: Project Euler 126 - Cuboid Layers
  2. Геометрия параллелепипеда: Wikipedia - Cuboid
  3. Площадь поверхности: Wikipedia - Surface area
  4. Арифметическая прогрессия: Wikipedia - Arithmetic progression
  5. Квадратичная функция: Wikipedia - Quadratic function

ملخص المشكلة

لدينا متوازي مستطيلات أبعاده \(a\) و\(b\) و\(c\)، وتُبنى حوله طبقات متتالية من مكعبات الوحدة. إذا رتبنا الأبعاد بحيث \(a \le b \le c\) وكتبنا رقم الطبقة على صورة \(\ell \ge 1\)، فإن \(L(a,b,c,\ell)\) تمثل عدد المكعبات في الطبقة رقم \(\ell\). المطلوب هو أصغر عدد \(m\) يظهر لدى 1000 حالة مرتبة من نوع «متوازي مستطيلات مع رقم طبقة».

ومن الملائم كتابة دالة العد كما يلي:

$$C(m)=\#\{(a,b,c,\ell): a \le b \le c,\ \ell \ge 1,\ L(a,b,c,\ell)=m\}.$$

إذن نبحث عن أصغر \(m\) يحقق \(C(m)=1000\). ترتيب الأبعاد \(a \le b \le c\) يزيل فقط التكرار الناتج عن تدوير الشكل؛ أما الأشكال المختلفة وأرقام الطبقات المختلفة فتبقى محسوبة كلٌّ على حدة.

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

جوهر الحل هو استخراج صيغة دقيقة لحجم الطبقة رقم \(\ell\)، ثم إجراء عد شامل لكل الحالات الصحيحة \((a,b,c,\ell)\).

الطبقة الأولى هي سطح الشكل نفسه

أول طبقة خارجية تغطي الأوجه الستة لمتوازي المستطيلات. يوجد وجهان مساحتهما \(ab\)، ووجهان مساحتهما \(ac\)، ووجهان مساحتهما \(bc\)، ولذلك

$$L(a,b,c,1)=2(ab+ac+bc).$$

وهذا هو أصغر حجم طبقة لذلك الشكل. لذا إذا كنا نبحث حتى حد أعلى \(m\)، فلا يلزم النظر إلا إلى الأبعاد التي تحقق \(2(ab+ac+bc) \le m\).

كيف تنمو طبقة إلى الطبقة التالية

الانتقال من الطبقة \(\ell\) إلى الطبقة \(\ell+1\) يعني إضافة إطار بسماكة مكعب واحد حول الغلاف الحالي. الجزء الخطي يأتي من الشرائط الممتدة على الحواف الاثنتي عشرة: كل طول من الأطوال الثلاثة يظهر أربع مرات، ولذلك يكون إسهام الحواف دائمًا \(4(a+b+c)\).

وهناك أيضًا إسهام ناتج عن الزوايا. بعد الطبقة الأولى تبدأ المناطق المتدرجة قرب الزوايا الثماني في الاتساع، وعند الانتقال من \(\ell\) إلى \(\ell+1\) تضيف هذه المناطق معًا \(8(\ell-1)\) مكعبات جديدة. ومن ثم نحصل على

$$L(a,b,c,\ell+1)-L(a,b,c,\ell)=4(a+b+c)+8(\ell-1)=4(a+b+c+2\ell-2).$$

إذن، لمتوازي مستطيلات ثابت، تكون فروق الطبقات المتتالية متتالية حسابية. وهذه الرتابة هي السبب المباشر الذي يسمح للحل البرمجي بإيقاف الحلقة الداخلية الخاصة بالطبقات بمجرد تجاوز الحد الحالي.

بجمع العلاقة التكرارية نحصل على الصيغة المغلقة

إذا بدأنا من \(L(a,b,c,1)=2(ab+ac+bc)\) وجمعنا الزيادة السابقة عدد \(\ell-1\) مرة، نحصل على

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c)+4(\ell-1)(\ell-2).$$

وغالبًا ما تُكتب آخر حدين على الصورة

$$L(a,b,c,\ell)=2(ab+ac+bc)+4(\ell-1)(a+b+c+\ell-2).$$

هذه هي الصيغة المستعملة فعليًا في تطبيقات C++ وPython وJava. ويمكن استنتاج عدة خصائص مباشرة منها: \(L(a,b,c,\ell)\) عدد زوجي دائمًا، وهو متزايد بدقة مع \(\ell\) عندما تكون \(a,b,c\) ثابتة، كما أنه متزايد أيضًا في كل من \(a\) و\(b\) و\(c\) عندما تثبت البقية.

مثال محسوب: متوازي المستطيلات \(1 \times 2 \times 3\)

عند \(a=1\) و\(b=2\) و\(c=3\)، فإن الطبقة الأولى تحتوي

$$L(1,2,3,1)=2(1\cdot2+1\cdot3+2\cdot3)=2(11)=22.$$

ثم تعطي العلاقة التكرارية

$$L(1,2,3,2)-L(1,2,3,1)=4(1+2+3)=24,$$

ومن ثم \(L(1,2,3,2)=46\). وفي الطبقة التالية تكبر الزيادة بمقدار 8:

$$L(1,2,3,3)-L(1,2,3,2)=4(1+2+3+2)=32,$$

فيكون \(L(1,2,3,3)=78\). وسلسلة الزيادات \(24,32,40,\dots\) توضح بجلاء البنية الخطية للفروق بين الطبقات.

من صيغة حالة واحدة إلى دالة العد العامة

بعد معرفة \(L(a,b,c,\ell)\)، تتحول المسألة إلى بناء جدول تكرارات. لكل قيمة \(m\)، نعد عدد الرباعيات \((a,b,c,\ell)\) التي تحقق \(L(a,b,c,\ell)=m\). وأول \(m\) يظهر بتكرار يساوي 1000 هو الجواب المطلوب.

لا حاجة هنا إلى أدوات نظرية أعمق. الفكرة الحاسمة هي أن فضاء الحالات الحقيقي يصبح قابلًا للعد المباشر والدقيق بمجرد استغلال الترتيب \(a \le b \le c\)، والرتابة في \(c\) و\(\ell\)، والشرط الضروري \(L(a,b,c,1) \le m\).

كيف يعمل الكود

عد جميع أحجام الطبقات حتى حد مؤقت

يختار التنفيذ أولًا حدًا علويًا مؤقتًا \(M\)، ثم ينشئ مصفوفة تكرارات للقيم من 0 إلى \(M\). بعد ذلك يجري على كل الثلاثيات المرتبة \(a \le b \le c\) التي لا تتجاوز طبقتها الأولى هذا الحد، ويحسب قيم \(L(a,b,c,\ell)\) عند \(\ell=1,2,3,\dots\). وكل قيمة لا تتجاوز \(M\) تزيد العداد الموافق لها بمقدار واحد.

الرتابة تسمح بإنهاء كل حلقة متداخلة مبكرًا

تتوقف حلقات \(a\) و\(b\) و\(c\) فور اللحظة التي تصبح فيها أصغر طبقة أولى ممكنة في ذلك الفرع أكبر من \(M\). أي أن الحلقة الخارجية لا تحتاج إلا إلى قيم \(a\) التي تحقق \(L(a,a,a,1) \le M\)؛ ومع تثبيت \(a\)، لا تحتاج الحلقة التالية إلا إلى قيم \(b\) التي تحقق \(L(a,b,b,1) \le M\)؛ ومع تثبيت \(a,b\)، تنتهي حلقة \(c\) عندما يصبح \(L(a,b,c,1) \gt M\). أما داخل متوازي مستطيلات معين، فتتوقف حلقة الطبقات عند أول \(\ell\) يجعل \(L(a,b,c,\ell) \gt M\).

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

توسيع نافذة البحث حتى ظهور التكرار المطلوب

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

ويتضمن أحد التطبيقات أيضًا فحصين بسيطين: \(L(1,1,1,1)=6\) والمثال المعروف \(C(154)=10\). وبهذا يتم التأكد من صحة صيغة الطبقات ومن منطق العد قبل الانتقال إلى الهدف النهائي 1000.

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

إذا كان الحد النهائي الناجح هو \(M\)، فإن مصفوفة التكرارات تستهلك ذاكرة \(O(M)\). كما أن المرور النهائي الذي يبحث عن أول قيمة ذات تكرار 1000 يحتاج إلى زمن \(O(M)\) أيضًا.

أما تكلفة التعداد فالأدق أن تُكتب بصيغة حساسة للمخرجات. لتكن \(A(M)\) مجموعة كل متوازيات المستطيلات المرتبة التي تحقق \(L(a,b,c,1) \le M\)، وليكن \(r(a,b,c;M)\) أكبر رقم طبقة يحقق \(L(a,b,c,\ell) \le M\). عندئذ تكون كلفة مرور عد واحد هي

$$O\!\left(M+\sum_{(a,b,c)\in A(M)} r(a,b,c;M)\right).$$

ولأن \(L(a,b,c,\ell)\) دالة تربيعية في \(\ell\)، فإن من الآمن كتابة \(r(a,b,c;M)=O(\sqrt{M})\) كحد أعلى عام. عمليًا يكون الأداء أفضل بكثير من هذا التقدير الخشن، لأن شروط الإيقاف الرتيبة في \(b\) و\(c\) و\(\ell\) تقص جزءًا كبيرًا من فضاء البحث. أما تكرار مضاعفة الحد فيؤثر فقط في الثابت ولا يغير السلوك الكلي.

هوامش ومراجع

  1. صفحة المسألة: Project Euler 126 - Cuboid Layers
  2. هندسة متوازي المستطيلات: Wikipedia - Cuboid
  3. المساحة السطحية: Wikipedia - Surface area
  4. المتتالية الحسابية: Wikipedia - Arithmetic progression
  5. الدالة التربيعية: Wikipedia - Quadratic function