Problem Summary

The goal is to evaluate

$$S(n)=\sum_{k=1}^{n}\sum_{p=1}^{n}(1-k^2)^p \pmod{M},\qquad M=10^9+7.$$

For the actual input \(n=10^6\), a direct double loop would require about \(10^{12}\) term updates, so the computation must be reorganized. The key observation is that for each fixed \(k\), the inner sum is a finite geometric series. Once that series is collapsed to a closed form, the problem becomes a single loop over \(k\), with every contribution evaluated by modular exponentiation.

A useful checkpoint is

$$S(4)=51160,$$

which the implementation uses to confirm that the closed form matches direct evaluation on a small case.

Mathematical Approach

The entire method comes from simplifying the inner sum for one fixed value of \(k\).

Step 1: Isolate the Inner Sum for One \(k\)

Define

$$u_k=1-k^2.$$

Then the contribution of this \(k\) to the outer sum is

$$T_k=\sum_{p=1}^{n}u_k^p.$$

Therefore

$$S(n)=\sum_{k=1}^{n}T_k.$$

This separates the problem into \(n\) independent geometric sums.

Step 2: Collapse the Geometric Series

For \(u_k\neq 1\), the usual finite geometric-series identity gives

$$T_k=\frac{u_k^{n+1}-u_k}{u_k-1}=u_k\cdot\frac{u_k^n-1}{u_k-1}.$$

This is exactly the form used by the implementation: one fast power computes \(u_k^n\), and the remaining work is a small number of modular multiplications.

Step 3: Simplify the Denominator

Because \(u_k=1-k^2\), we have

$$u_k-1=-k^2.$$

So the same quantity can also be written as

$$T_k=\frac{u_k-u_k^{n+1}}{k^2}.$$

This makes the arithmetic easier to interpret: the denominator is just \(k^2\). Under the Euler modulus \(M=10^9+7\), every \(k\) in the range \(1\le k\le 10^6\) is nonzero modulo \(M\), so the denominator is invertible for the actual problem input.

Step 4: Replace Division by a Modular Inverse

All calculations are performed modulo the prime \(M\). For any nonzero residue \(a\), Fermat's little theorem gives

$$a^{-1}\equiv a^{M-2}\pmod{M}.$$

Hence, for \(u_k\not\equiv 1\pmod{M}\),

$$T_k\equiv u_k\,(u_k^n-1)\,(u_k-1)^{-1}\pmod{M}.$$

This turns the whole computation into repeated modular exponentiation and multiplication, which is far cheaper than summing \(n\) powers for each \(k\).

Step 5: Handle the Degenerate Case Safely

If \(u_k\equiv 1\pmod{M}\), then every term in the inner sum equals \(1\), so

$$T_k\equiv n\pmod{M}.$$

For the actual Project Euler parameters this case does not occur, because \(u_k\equiv 1\) would imply \(k^2\equiv 0\pmod{M}\), impossible for \(1\le k<M\). Still, the implementation keeps this branch so the formula never attempts to divide by zero modulo \(M\).

Worked Example: \(n=4\)

Now compute the checkpoint directly.

For \(k=1\), \(u_1=1-1^2=0\), so

$$T_1=0+0+0+0=0.$$

For \(k=2\), \(u_2=-3\), hence

$$T_2=(-3)+(-3)^2+(-3)^3+(-3)^4=-3+9-27+81=60.$$

For \(k=3\), \(u_3=-8\), so

$$T_3=-8+64-512+4096=3640.$$

For \(k=4\), \(u_4=-15\), therefore

$$T_4=-15+225-3375+50625=47460.$$

Adding the four contributions gives

$$S(4)=0+60+3640+47460=51160,$$

which matches the checkpoint used by the implementation.

How the Code Works

The C++, Python, and Java implementations all follow the same mathematical structure. They iterate once over \(k=1,2,\dots,n\), reduce \(1-k^2\) into the range \(0\) to \(M-1\), and then evaluate the closed form for \(T_k\) modulo \(M\).

For each \(k\), the implementation computes the power \(u_k^n \bmod M\) with binary exponentiation. If the denominator is nonzero, it computes the modular inverse by raising that denominator to the power \(M-2\), again with binary exponentiation. The resulting contribution is added to the running total modulo \(M\).

One implementation also includes two small validation steps: the known checkpoint \(S(4)=51160\), and a comparison between the optimized formula and direct summation for a small input. Those checks confirm that the algebraic reduction is correct before the large target value is evaluated.

Complexity Analysis

There is one outer loop over \(k=1\) to \(n\). For each \(k\), the implementation performs a constant amount of arithmetic plus modular exponentiation for \(u_k^n\), and modular exponentiation for the inverse. Binary exponentiation costs \(O(\log n)\) for the first power and \(O(\log M)\) for the inverse, so the full running time is

$$O\bigl(n(\log n+\log M)\bigr).$$

With the fixed Euler modulus \(M=10^9+7\), this is effectively \(O(n\log n)\). The memory usage is

$$O(1),$$

since the program stores only a handful of current residues. This is a major improvement over the naive \(O(n^2)\) direct summation.

Footnotes and References

  1. Problem page: https://projecteuler.net/problem=479
  2. Geometric series: Wikipedia — Geometric series
  3. Fermat's little theorem: Wikipedia — Fermat's little theorem
  4. Modular exponentiation: Wikipedia — Modular exponentiation

Problemzusammenfassung

Gesucht ist der Wert

$$S(n)=\sum_{k=1}^{n}\sum_{p=1}^{n}(1-k^2)^p \pmod{M},\qquad M=10^9+7.$$

Für den eigentlichen Eingabewert \(n=10^6\) wäre eine direkte doppelte Schleife viel zu langsam, denn sie würde etwa \(10^{12}\) Einzelterme auswerten. Die Lösung nutzt deshalb die Tatsache, dass die innere Summe für festes \(k\) eine endliche geometrische Reihe ist. Dadurch wird aus dem quadratischen Verfahren eine einfache Schleife über \(k\), in der jeder Beitrag mit modularer Potenzierung berechnet wird.

Ein wichtiger Kontrollwert ist

$$S(4)=51160,$$

und genau dieser Wert wird verwendet, um die geschlossene Formel an einem kleinen Beispiel zu überprüfen.

Mathematischer Ansatz

Der gesamte Trick besteht darin, die innere Summe für ein festes \(k\) algebraisch zusammenzufassen.

Schritt 1: Die innere Summe für ein festes \(k\) isolieren

Definiere

$$u_k=1-k^2.$$

Dann ist der Beitrag dieses \(k\) zur Gesamtsumme

$$T_k=\sum_{p=1}^{n}u_k^p.$$

Somit gilt

$$S(n)=\sum_{k=1}^{n}T_k.$$

Das Problem zerfällt also in \(n\) voneinander unabhängige geometrische Reihen.

Schritt 2: Die geometrische Reihe schließen

Für \(u_k\neq 1\) liefert die bekannte Formel für endliche geometrische Reihen

$$T_k=\frac{u_k^{n+1}-u_k}{u_k-1}=u_k\cdot\frac{u_k^n-1}{u_k-1}.$$

Genau diese Form wird in der Implementierung ausgewertet: Eine schnelle Potenz liefert \(u_k^n\), der Rest sind nur noch wenige modulare Multiplikationen.

Schritt 3: Den Nenner vereinfachen

Aus \(u_k=1-k^2\) folgt unmittelbar

$$u_k-1=-k^2.$$

Damit kann derselbe Ausdruck auch geschrieben werden als

$$T_k=\frac{u_k-u_k^{n+1}}{k^2}.$$

So sieht man klar, dass im Nenner nur \(k^2\) steht. Unter dem Euler-Modulus \(M=10^9+7\) ist für alle \(1\le k\le 10^6\) dieser Nenner invertierbar, denn kein solches \(k\) ist modulo \(M\) gleich null.

Schritt 4: Division durch modulares Inverses ersetzen

Da \(M\) prim ist, gilt für jede von null verschiedene Restklasse \(a\)

$$a^{-1}\equiv a^{M-2}\pmod{M}.$$

Also erhält man für \(u_k\not\equiv 1\pmod{M}\)

$$T_k\equiv u_k\,(u_k^n-1)\,(u_k-1)^{-1}\pmod{M}.$$

Damit besteht die gesamte Berechnung nur noch aus modularer Potenzierung und modularer Multiplikation, statt aus \(n\) einzelnen Potenzen pro \(k\).

Schritt 5: Den Sonderfall sauber behandeln

Falls \(u_k\equiv 1\pmod{M}\), ist jeder Summand der inneren Summe gleich \(1\), also

$$T_k\equiv n\pmod{M}.$$

Für die echten Problemparameter tritt dieser Fall nicht ein, denn \(u_k\equiv 1\) würde \(k^2\equiv 0\pmod{M}\) bedeuten, was für \(1\le k<M\) unmöglich ist. Die Implementierung behält diesen Zweig trotzdem bei, damit niemals durch null modulo \(M\) dividiert wird.

Durchgerechnetes Beispiel: \(n=4\)

Nun berechnen wir den Kontrollwert direkt.

Für \(k=1\) ist \(u_1=0\), also

$$T_1=0+0+0+0=0.$$

Für \(k=2\) gilt \(u_2=-3\), damit

$$T_2=(-3)+(-3)^2+(-3)^3+(-3)^4=-3+9-27+81=60.$$

Für \(k=3\) ist \(u_3=-8\), also

$$T_3=-8+64-512+4096=3640.$$

Für \(k=4\) erhält man \(u_4=-15\) und damit

$$T_4=-15+225-3375+50625=47460.$$

Die Summe aller Beiträge ist

$$S(4)=0+60+3640+47460=51160,$$

genau der Kontrollwert der Implementierung.

Wie der Code arbeitet

Die C++-, Python- und Java-Implementierungen folgen alle derselben mathematischen Struktur. Sie durchlaufen einmal \(k=1,2,\dots,n\), bringen \(1-k^2\) zunächst in den Restklassenbereich \(0\) bis \(M-1\) und berechnen danach den geschlossenen Ausdruck für \(T_k\) modulo \(M\).

Für jedes \(k\) wird \(u_k^n \bmod M\) mit binärer Potenzierung berechnet. Ist der Nenner ungleich null, wird sein modulares Inverses über die Potenz \(M-2\) bestimmt, wiederum mit binärer Potenzierung. Anschließend wird der Beitrag zu einer laufenden Summe modulo \(M\) addiert.

Eine der Implementierungen enthält zusätzlich zwei kleine Prüfungen: den bekannten Wert \(S(4)=51160\) sowie einen Vergleich zwischen geschlossener Formel und direkter Auswertung für eine kleine Eingabe. Dadurch wird bestätigt, dass die algebraische Umformung korrekt implementiert ist.

Komplexitätsanalyse

Es gibt genau eine äußere Schleife über \(k=1\) bis \(n\). Für jedes \(k\) fallen nur konstant viele modulare Multiplikationen sowie eine Potenzierung für \(u_k^n\) und eine Potenzierung für das Inverse an. Mit binärer Potenzierung ergibt das

$$O\bigl(n(\log n+\log M)\bigr)$$

Zeit. Da \(M=10^9+7\) fest ist, kann man dies praktisch als

$$O(n\log n)$$

auffassen. Der Speicherbedarf ist

$$O(1),$$

weil nur wenige aktuelle Restklassen gehalten werden. Gegenüber der naiven \(O(n^2)\)-Methode ist das ein entscheidender Gewinn.

Fußnoten und Quellen

  1. Problemseite: https://projecteuler.net/problem=479
  2. Geometrische Reihe: Wikipedia — Geometrische Reihe
  3. Kleiner Satz von Fermat: Wikipedia — Kleiner Satz von Fermat
  4. Modulare Exponentiation: Wikipedia — Modulare Exponentiation

Problem Özeti

Hedef,

$$S(n)=\sum_{k=1}^{n}\sum_{p=1}^{n}(1-k^2)^p \pmod{M},\qquad M=10^9+7$$

değerini hesaplamaktır. Gerçek girdi \(n=10^6\) olduğundan, doğrudan çift döngüyle ilerlemek yaklaşık \(10^{12}\) terim üretir ve pratik değildir. Çözümün temel gözlemi, sabit bir \(k\) için iç toplamın sonlu bir geometrik seri olmasıdır. Bu seri kapalı forma indirildiğinde problem, her katkının modüler üs alma ile bulunduğu tek bir dış döngüye dönüşür.

Önemli bir kontrol değeri şudur:

$$S(4)=51160.$$

Uygulama, küçük bir örnekte kapalı formun doğru çalıştığını bu değerle doğrular.

Matematiksel Yaklaşım

Bütün yöntem, tek bir \(k\) değeri için iç toplamı sadeleştirmeye dayanır.

Adım 1: Sabit bir \(k\) için iç toplamı ayır

Şunu tanımlayalım:

$$u_k=1-k^2.$$

Bu durumda ilgili \(k\)'nin dış toplama katkısı

$$T_k=\sum_{p=1}^{n}u_k^p$$

olur. Dolayısıyla

$$S(n)=\sum_{k=1}^{n}T_k$$

yazabiliriz. Böylece problem \(n\) bağımsız geometrik seri hesabına ayrılır.

Adım 2: Geometrik seriyi kapalı forma indir

\(u_k\neq 1\) için sonlu geometrik seri formülü

$$T_k=\frac{u_k^{n+1}-u_k}{u_k-1}=u_k\cdot\frac{u_k^n-1}{u_k-1}$$

sonucunu verir. Uygulama tam olarak bu ifadeyi kullanır: önce \(u_k^n\) hızlı üs alma ile bulunur, sonra birkaç modüler çarpma yapılır.

Adım 3: Paydayı daha anlaşılır hale getir

\(u_k=1-k^2\) olduğundan

$$u_k-1=-k^2$$

elde edilir. Aynı ifade bu yüzden

$$T_k=\frac{u_k-u_k^{n+1}}{k^2}$$

şeklinde de yazılabilir. Bu görünüm, paydanın yalnızca \(k^2\) olduğunu açıkça gösterir. Euler modülü \(M=10^9+7\) altında ve \(1\le k\le 10^6\) aralığında, hiçbir \(k\) mod \(M\)'de sıfır olmadığı için bu payda terslenebilir.

Adım 4: Bölme yerine modüler ters kullan

\(M\) asal olduğundan, sıfır olmayan her \(a\) için

$$a^{-1}\equiv a^{M-2}\pmod{M}$$

vardır. Bu nedenle \(u_k\not\equiv1\pmod{M}\) iken

$$T_k\equiv u_k\,(u_k^n-1)\,(u_k-1)^{-1}\pmod{M}$$

yazılır. Böylece her \(k\) için \(n\) tane kuvvet toplamak yerine yalnızca modüler üs alma ve çarpma yapmak yeterli olur.

Adım 5: Özel durumu güvenli biçimde ele al

Eğer \(u_k\equiv1\pmod{M}\) ise iç toplamın her terimi \(1\) olur ve

$$T_k\equiv n\pmod{M}$$

elde edilir. Gerçek Euler girdisinde bu durum yaşanmaz; çünkü \(u_k\equiv1\) olması \(k^2\equiv0\pmod{M}\) anlamına gelir ve \(1\le k<M\) iken bu imkansızdır. Yine de uygulama, modüler olarak sıfıra bölme ihtimalini tamamen ortadan kaldırmak için bu dalı korur.

Çalışılmış Örnek: \(n=4\)

Şimdi kontrol değerini doğrudan hesaplayalım.

\(k=1\) için \(u_1=0\), dolayısıyla

$$T_1=0+0+0+0=0.$$

\(k=2\) için \(u_2=-3\), o halde

$$T_2=(-3)+(-3)^2+(-3)^3+(-3)^4=-3+9-27+81=60.$$

\(k=3\) için \(u_3=-8\), bu da

$$T_3=-8+64-512+4096=3640$$

verir. \(k=4\) için \(u_4=-15\) olduğundan

$$T_4=-15+225-3375+50625=47460.$$

Toplayınca

$$S(4)=0+60+3640+47460=51160$$

elde edilir; bu da uygulamanın kullandığı kontrol değeriyle aynıdır.

Kod Nasıl Çalışır

C++, Python ve Java uygulamaları aynı matematiksel planı izler. Önce \(k=1,2,\dots,n\) üzerinde tek bir döngü kurulur. Her adımda \(1-k^2\) ifadesi mod \(M\) altında \(0\) ile \(M-1\) arasına taşınır ve ardından \(T_k\) için elde edilen kapalı form değerlendirilir.

Her \(k\) için \(u_k^n \bmod M\) değeri ikili üs alma ile hesaplanır. Payda sıfır değilse modüler tersi yine ikili üs alma ile, bu kez üs \(M-2\) kullanılarak bulunur. Son katkı çarpılıp toplam cevaba mod \(M\) altında eklenir.

Bir uygulama ayrıca iki küçük doğrulama içerir: \(S(4)=51160\) kontrolü ve küçük bir girişte kapalı formun doğrudan toplamla karşılaştırılması. Böylece cebirsel dönüşümün doğru uygulandığı teyit edilir.

Karmaşıklık Analizi

Dışarıda yalnızca \(k=1\) ile \(n\) arasında tek bir döngü vardır. Her \(k\) için sabit sayıda modüler işlem, bir adet \(u_k^n\) hesabı ve bir adet modüler ters hesabı yapılır. İkili üs alma kullanıldığında toplam süre

$$O\bigl(n(\log n+\log M)\bigr)$$

olur. Sabit Euler modülü \(M=10^9+7\) için bu pratikte

$$O(n\log n)$$

olarak görülebilir. Bellek kullanımı

$$O(1)$$

seviyesindedir; çünkü yalnızca birkaç geçici modüler değer saklanır. Bu, doğrudan \(O(n^2)\) yönteme göre çok büyük bir iyileşmedir.

Dipnotlar ve Kaynaklar

  1. Problem sayfası: https://projecteuler.net/problem=479
  2. Geometrik seri: Wikipedia — Geometrik seri
  3. Fermat'nın küçük teoremi: Wikipedia — Fermat'nın küçük teoremi
  4. Modüler üst alma: Wikipedia — Modular exponentiation

Resumen del Problema

Se quiere calcular

$$S(n)=\sum_{k=1}^{n}\sum_{p=1}^{n}(1-k^2)^p \pmod{M},\qquad M=10^9+7.$$

Para el valor real \(n=10^6\), una evaluación directa con dos bucles sería inviable, porque implicaría alrededor de \(10^{12}\) actualizaciones de términos. La observación decisiva es que, para cada \(k\) fijo, la suma interior es una serie geométrica finita. Al reemplazar esa serie por una fórmula cerrada, todo el problema se reduce a un único recorrido sobre \(k\), donde cada contribución se obtiene mediante potenciación modular.

Un punto de control muy útil es

$$S(4)=51160,$$

que permite verificar en un caso pequeño que la fórmula cerrada coincide con la suma directa.

Enfoque Matemático

Todo el método nace de simplificar la suma interior para un valor fijo de \(k\).

Paso 1: Aislar la suma interior para un \(k\) fijo

Definimos

$$u_k=1-k^2.$$

Entonces la contribución correspondiente a ese \(k\) es

$$T_k=\sum_{p=1}^{n}u_k^p.$$

Por tanto,

$$S(n)=\sum_{k=1}^{n}T_k.$$

Así el problema queda dividido en \(n\) series geométricas independientes.

Paso 2: Cerrar la serie geométrica

Si \(u_k\neq 1\), la identidad usual para una serie geométrica finita da

$$T_k=\frac{u_k^{n+1}-u_k}{u_k-1}=u_k\cdot\frac{u_k^n-1}{u_k-1}.$$

Ésta es exactamente la forma que usan las implementaciones: una potenciación rápida produce \(u_k^n\) y el resto son unas pocas multiplicaciones modulares.

Paso 3: Simplificar el denominador

Como \(u_k=1-k^2\), se tiene

$$u_k-1=-k^2.$$

Por eso la misma expresión también puede escribirse como

$$T_k=\frac{u_k-u_k^{n+1}}{k^2}.$$

Esta forma deja claro que el denominador es simplemente \(k^2\). Bajo el módulo primo \(M=10^9+7\), todo \(k\) del rango real \(1\le k\le 10^6\) es no nulo módulo \(M\), de modo que el inverso existe.

Paso 4: Sustituir la división por un inverso modular

Como \(M\) es primo, para cualquier residuo no nulo \(a\) vale

$$a^{-1}\equiv a^{M-2}\pmod{M}.$$

En consecuencia, cuando \(u_k\not\equiv 1\pmod{M}\),

$$T_k\equiv u_k\,(u_k^n-1)\,(u_k-1)^{-1}\pmod{M}.$$

Esto reemplaza una suma de \(n\) potencias por una pequeña cantidad de exponenciaciones y multiplicaciones modulares.

Paso 5: Tratar con seguridad el caso degenerado

Si \(u_k\equiv 1\pmod{M}\), entonces todos los términos de la suma interior valen \(1\), y por tanto

$$T_k\equiv n\pmod{M}.$$

Para los parámetros reales del problema ese caso no aparece, porque \(u_k\equiv 1\) implicaría \(k^2\equiv 0\pmod{M}\), imposible cuando \(1\le k<M\). Aun así, la implementación conserva esta rama para evitar cualquier división por cero modular.

Ejemplo Trabajado: \(n=4\)

Calculemos ahora el valor de control.

Para \(k=1\), \(u_1=0\), luego

$$T_1=0+0+0+0=0.$$

Para \(k=2\), \(u_2=-3\), así que

$$T_2=(-3)+(-3)^2+(-3)^3+(-3)^4=-3+9-27+81=60.$$

Para \(k=3\), \(u_3=-8\), por lo que

$$T_3=-8+64-512+4096=3640.$$

Para \(k=4\), \(u_4=-15\), y entonces

$$T_4=-15+225-3375+50625=47460.$$

Sumando todas las contribuciones se obtiene

$$S(4)=0+60+3640+47460=51160,$$

exactamente el valor de control usado por la implementación.

Cómo Funciona el Código

Las implementaciones en C++, Python y Java siguen la misma estructura matemática. Recorren una sola vez \(k=1,2,\dots,n\), reducen \(1-k^2\) al intervalo \(0\) a \(M-1\), y después evalúan la fórmula cerrada de \(T_k\) módulo \(M\).

Para cada \(k\), la implementación calcula \(u_k^n \bmod M\) mediante exponenciación binaria. Si el denominador no es cero, obtiene el inverso modular elevándolo a la potencia \(M-2\), otra vez con exponenciación binaria. Luego añade la contribución resultante al acumulado total módulo \(M\).

Una de las implementaciones también incluye dos comprobaciones pequeñas: el valor conocido \(S(4)=51160\) y una comparación entre la fórmula optimizada y la suma directa para una entrada reducida. Eso confirma que la transformación algebraica se programó correctamente.

Análisis de Complejidad

Existe un único bucle exterior sobre \(k=1\) hasta \(n\). Para cada \(k\) se hacen unas pocas operaciones modulares, una potenciación para \(u_k^n\) y otra para el inverso. Con exponenciación binaria, el coste total es

$$O\bigl(n(\log n+\log M)\bigr).$$

Como \(M=10^9+7\) es fijo, esto equivale en la práctica a

$$O(n\log n).$$

La memoria utilizada es

$$O(1),$$

ya que sólo se mantienen unas pocas cantidades modulares corrientes. Es una mejora muy grande frente al enfoque directo de orden \(O(n^2)\).

Notas y Referencias

  1. Página del problema: https://projecteuler.net/problem=479
  2. Serie geométrica: Wikipedia — Progresión geométrica
  3. Pequeño teorema de Fermat: Wikipedia — Pequeño teorema de Fermat
  4. Exponenciación modular: Wikipedia — Exponenciación modular

问题概述

目标是计算

$$S(n)=\sum_{k=1}^{n}\sum_{p=1}^{n}(1-k^2)^p \pmod{M},\qquad M=10^9+7$$

当真实输入为 \(n=10^6\) 时,直接按照定义做双重循环需要大约 \(10^{12}\) 次项更新,显然不可行。解法的关键观察是:对每个固定的 \(k\),内层求和都是一个有限等比级数。只要把这段等比级数改写成闭式,整个问题就从 \(O(n^2)\) 的直接求和,转化为只对 \(k\) 做一次扫描,并在每一步用模幂计算当前贡献。

实现里还使用了一个很重要的校验值:

$$S(4)=51160$$

这个小样例可以用来确认闭式公式与直接求和完全一致。

数学方法

整个方法都来自于对固定 \(k\) 时内层和式的代数化简。

步骤 1:先固定一个 \(k\),单独研究内层和

定义

$$u_k=1-k^2$$

那么这个 \(k\) 对总和的贡献就是

$$T_k=\sum_{p=1}^{n}u_k^p$$

于是总和可以写成

$$S(n)=\sum_{k=1}^{n}T_k$$

这样一来,原问题就被拆成了 \(n\) 个彼此独立的等比级数。

步骤 2:把等比级数化成闭式

当 \(u_k\neq 1\) 时,有限等比级数公式给出

$$T_k=\frac{u_k^{n+1}-u_k}{u_k-1}=u_k\cdot\frac{u_k^n-1}{u_k-1}$$

实现正是按这个形式来计算的:先用快速幂求出 \(u_k^n\),然后再做少量模乘即可得到当前项。

步骤 3:把分母改写成更直观的形式

因为 \(u_k=1-k^2\),所以

$$u_k-1=-k^2$$

因此同一个表达式也可以改写成

$$T_k=\frac{u_k-u_k^{n+1}}{k^2}$$

这个写法更容易解释代码里的除法步骤,因为分母其实只是 \(k^2\)。在题目指定的模数 \(M=10^9+7\) 下,真实输入满足 \(1\le k\le 10^6<M\),所以 \(k\) 不可能在模 \(M\) 意义下等于零,分母自然总是可逆的。

步骤 4:用模逆代替除法

由于 \(M\) 是素数,对任意非零剩余类 \(a\) 都有

$$a^{-1}\equiv a^{M-2}\pmod{M}$$

因此,只要 \(u_k\not\equiv 1\pmod{M}\),就可以写成

$$T_k\equiv u_k\,(u_k^n-1)\,(u_k-1)^{-1}\pmod{M}$$

这一步把“对每个 \(k\) 再求一遍长度为 \(n\) 的幂和”变成了“做几次模幂和模乘”,计算量立刻下降了几个数量级。

步骤 5:安全处理退化情形

如果 \(u_k\equiv 1\pmod{M}\),那么内层每一项都等于 \(1\),于是

$$T_k\equiv n\pmod{M}$$

对于本题真实参数,这种情况其实不会出现,因为 \(u_k\equiv 1\) 等价于 \(k^2\equiv 0\pmod{M}\),而 \(1\le k<M\) 时这是不可能的。不过实现仍然保留这一分支,从而在更一般的情形下也不会发生模意义下的零除。

例子:\(n=4\) 的完整计算

下面直接算出校验值。

当 \(k=1\) 时,\(u_1=0\),所以

$$T_1=0+0+0+0=0$$

当 \(k=2\) 时,\(u_2=-3\),因此

$$T_2=(-3)+(-3)^2+(-3)^3+(-3)^4=-3+9-27+81=60$$

当 \(k=3\) 时,\(u_3=-8\),于是

$$T_3=-8+64-512+4096=3640$$

当 \(k=4\) 时,\(u_4=-15\),所以

$$T_4=-15+225-3375+50625=47460$$

把四项相加得到

$$S(4)=0+60+3640+47460=51160$$

这正好与实现中的检查结果一致。

代码如何工作

C++、Python 和 Java 实现遵循完全相同的数学流程。它们只保留一层对 \(k=1,2,\dots,n\) 的循环;在每次循环中,先把 \(1-k^2\) 归约到 \(0\) 到 \(M-1\) 的标准模表示,再根据上面的闭式公式求出当前 \(T_k\) 的贡献。

对每个 \(k\),实现首先用二进制快速幂求出 \(u_k^n \bmod M\)。如果分母非零,再通过把分母提升到 \(M-2\) 次方来得到模逆,这一步同样使用快速幂。随后把当前贡献加到答案中,并在每一步都取模。

其中一个实现还包含两道小规模验证:一是检查 \(S(4)=51160\),二是在较小输入上把优化公式与直接求和结果逐项对比。这样就能确认代码确实实现了同一个数学公式,而不是只在大输入上“看起来合理”。

复杂度分析

外层只有一重循环,共执行 \(n\) 次。每个 \(k\) 需要常数次模运算、一次求 \(u_k^n\) 的快速幂、以及一次求模逆的快速幂。因此总时间复杂度是

$$O\bigl(n(\log n+\log M)\bigr)$$

由于题目里的模数固定为 \(M=10^9+7\),实际可以把它看成

$$O(n\log n)$$

空间复杂度为

$$O(1)$$

因为程序只保存少量当前模值,不需要任何与 \(n\) 成比例的额外数组。这比直接做双重求和的 \(O(n^2)\) 方法高效得多。

脚注与参考资料

  1. 题目页面:https://projecteuler.net/problem=479
  2. 等比级数:Wikipedia — 等比数列
  3. 费马小定理:Wikipedia — 费马小定理
  4. 模幂运算:Wikipedia — Modular exponentiation

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

Требуется вычислить

$$S(n)=\sum_{k=1}^{n}\sum_{p=1}^{n}(1-k^2)^p \pmod{M},\qquad M=10^9+7.$$

При реальном значении \(n=10^6\) прямой двойной цикл был бы слишком дорогим, потому что пришлось бы обработать около \(10^{12}\) слагаемых. Ключевое наблюдение состоит в том, что при фиксированном \(k\) внутренняя сумма является конечной геометрической прогрессией. После сведения этой прогрессии к замкнутой формуле задача превращается в один проход по \(k\), а каждый вклад вычисляется с помощью модульного возведения в степень.

Важная контрольная точка:

$$S(4)=51160,$$

и именно этот результат используется для проверки формулы на малом примере.

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

Весь метод строится на алгебраическом упрощении внутренней суммы для одного фиксированного \(k\).

Шаг 1: Выделим внутреннюю сумму для фиксированного \(k\)

Обозначим

$$u_k=1-k^2.$$

Тогда вклад данного \(k\) во внешнюю сумму равен

$$T_k=\sum_{p=1}^{n}u_k^p.$$

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

$$S(n)=\sum_{k=1}^{n}T_k.$$

То есть исходная задача распадается на \(n\) независимых геометрических сумм.

Шаг 2: Свернём геометрическую прогрессию

Если \(u_k\neq 1\), стандартная формула для конечной геометрической прогрессии даёт

$$T_k=\frac{u_k^{n+1}-u_k}{u_k-1}=u_k\cdot\frac{u_k^n-1}{u_k-1}.$$

Именно в таком виде выражение вычисляется в реализации: одно быстрое возведение в степень даёт \(u_k^n\), после чего остаётся несколько модульных умножений.

Шаг 3: Упростим знаменатель

Так как \(u_k=1-k^2\), имеем

$$u_k-1=-k^2.$$

Поэтому тот же вклад можно записать и так:

$$T_k=\frac{u_k-u_k^{n+1}}{k^2}.$$

Эта форма особенно наглядна: знаменатель равен просто \(k^2\). При модуле задачи \(M=10^9+7\) и диапазоне \(1\le k\le 10^6\) никакое такое \(k\) не равно нулю по модулю \(M\), значит знаменатель всегда обратим.

Шаг 4: Заменим деление модульным обратным

Поскольку \(M\) простое, для любого ненулевого остатка \(a\) выполняется

$$a^{-1}\equiv a^{M-2}\pmod{M}.$$

Значит, при \(u_k\not\equiv 1\pmod{M}\) можно вычислять

$$T_k\equiv u_k\,(u_k^n-1)\,(u_k-1)^{-1}\pmod{M}.$$

Тем самым внутренняя сумма длины \(n\) заменяется двумя быстрыми степенями и несколькими модульными операциями.

Шаг 5: Аккуратно обработаем вырожденный случай

Если \(u_k\equiv 1\pmod{M}\), то каждое слагаемое внутренней суммы равно \(1\), и поэтому

$$T_k\equiv n\pmod{M}.$$

Для реальных параметров Euler этот случай не возникает, потому что \(u_k\equiv 1\) означало бы \(k^2\equiv 0\pmod{M}\), а это невозможно при \(1\le k<M\). Тем не менее реализация сохраняет отдельную ветку, чтобы никогда не делить на ноль по модулю.

Подробный пример: \(n=4\)

Вычислим контрольное значение напрямую.

Для \(k=1\) имеем \(u_1=0\), значит

$$T_1=0+0+0+0=0.$$

Для \(k=2\) получаем \(u_2=-3\), следовательно

$$T_2=(-3)+(-3)^2+(-3)^3+(-3)^4=-3+9-27+81=60.$$

Для \(k=3\) имеем \(u_3=-8\), откуда

$$T_3=-8+64-512+4096=3640.$$

Для \(k=4\) выходит \(u_4=-15\), поэтому

$$T_4=-15+225-3375+50625=47460.$$

Сумма всех вкладов равна

$$S(4)=0+60+3640+47460=51160,$$

что точно совпадает с контрольным значением из реализации.

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

Реализации на C++, Python и Java используют одну и ту же математическую схему. Они выполняют единственный проход по \(k=1,2,\dots,n\), приводят выражение \(1-k^2\) к стандартному остатку от \(0\) до \(M-1\), а затем вычисляют вклад \(T_k\) по замкнутой формуле.

Для каждого \(k\) программа сначала находит \(u_k^n \bmod M\) методом бинарного возведения в степень. Если знаменатель не равен нулю, затем вычисляется модульный обратный как степень \(M-2\), опять же бинарным методом. После этого текущий вклад прибавляется к накопленному ответу по модулю \(M\).

Одна из реализаций дополнительно содержит две маленькие проверки: значение \(S(4)=51160\) и сравнение оптимизированной формулы с прямым суммированием на небольшом входе. Это подтверждает корректность алгебраического преобразования.

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

Есть ровно один внешний цикл по \(k\) от \(1\) до \(n\). Для каждого \(k\) выполняется постоянное число модульных операций, одно быстрое возведение в степень для \(u_k^n\) и ещё одно для вычисления обратного элемента. Поэтому полное время работы равно

$$O\bigl(n(\log n+\log M)\bigr).$$

Так как в задаче модуль фиксирован и равен \(M=10^9+7\), это практически эквивалентно

$$O(n\log n).$$

Память требуется лишь

$$O(1),$$

поскольку хранятся только несколько текущих остатков. Это гораздо лучше прямого подхода порядка \(O(n^2)\).

Сноски и ссылки

  1. Страница задачи: https://projecteuler.net/problem=479
  2. Геометрическая прогрессия: Wikipedia — Геометрическая прогрессия
  3. Малая теорема Ферма: Wikipedia — Малая теорема Ферма
  4. Модульное возведение в степень: Wikipedia — Modular exponentiation

ملخص المسألة

المطلوب هو حساب

$$S(n)=\sum_{k=1}^{n}\sum_{p=1}^{n}(1-k^2)^p \pmod{M},\qquad M=10^9+7.$$

عندما يكون الإدخال الحقيقي \(n=10^6\)، فإن التنفيذ المباشر بحلقتين متداخلتين يحتاج تقريبًا إلى \(10^{12}\) تحديثًا، وهذا غير عملي. الفكرة الأساسية هي أن المجموع الداخلي، عند تثبيت \(k\)، عبارة عن متتالية هندسية منتهية. وبعد تحويل هذا المجموع إلى صيغة مغلقة، تصبح المسألة مجرد حلقة واحدة على \(k\)، مع حساب كل مساهمة باستعمال الأسّ المعياري السريع.

هناك قيمة تحقق مهمة هي

$$S(4)=51160,$$

وتُستخدم للتأكد من أن الصيغة المغلقة تطابق الجمع المباشر في حالة صغيرة.

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

تعتمد الطريقة كلها على تبسيط المجموع الداخلي لقيمة ثابتة من \(k\).

الخطوة 1: عزل المجموع الداخلي عند تثبيت \(k\)

لنعرّف

$$u_k=1-k^2.$$

عندئذ تصبح مساهمة هذا \(k\) في المجموع الخارجي

$$T_k=\sum_{p=1}^{n}u_k^p.$$

وبالتالي

$$S(n)=\sum_{k=1}^{n}T_k.$$

أي أن المسألة الأصلية تنقسم إلى \(n\) مجاميع هندسية مستقلة.

الخطوة 2: إغلاق السلسلة الهندسية

إذا كان \(u_k\neq 1\)، فإن صيغة المجموع الهندسي المنتهي تعطي

$$T_k=\frac{u_k^{n+1}-u_k}{u_k-1}=u_k\cdot\frac{u_k^n-1}{u_k-1}.$$

وهذه هي الصيغة نفسها التي تعتمدها التطبيقات: نحسب أولًا \(u_k^n\) بالأسّ السريع، ثم نُكمل بعدد قليل من الضربات المعيارية.

الخطوة 3: تبسيط المقام

بما أن \(u_k=1-k^2\)، فإن

$$u_k-1=-k^2.$$

ولهذا يمكن أيضًا كتابة المقدار نفسه على الصورة

$$T_k=\frac{u_k-u_k^{n+1}}{k^2}.$$

هذه الصيغة توضح أن المقام ليس إلا \(k^2\). ومع المودولو الأولي \(M=10^9+7\)، وكل \(k\) في المجال الحقيقي \(1\le k\le 10^6\)، لا يمكن أن يكون \(k\equiv0\pmod{M}\)، لذلك يكون المقام قابلاً للعكس دائمًا في الإدخال الفعلي.

الخطوة 4: استبدال القسمة بالمعكوس الضربي

لأن \(M\) عدد أولي، فإن كل باقي غير صفري \(a\) يحقق

$$a^{-1}\equiv a^{M-2}\pmod{M}.$$

ومن ثم، عندما \(u_k\not\equiv1\pmod{M}\)، نحصل على

$$T_k\equiv u_k\,(u_k^n-1)\,(u_k-1)^{-1}\pmod{M}.$$

وبذلك نتحول من جمع \(n\) حدود لكل \(k\) إلى عدد صغير من عمليات الأسّ المعياري والضرب المعياري.

الخطوة 5: معالجة الحالة الخاصة بأمان

إذا تحقق \(u_k\equiv1\pmod{M}\)، فإن كل حد في المجموع الداخلي يساوي \(1\)، وبالتالي

$$T_k\equiv n\pmod{M}.$$

هذه الحالة لا تظهر في معطيات المسألة الفعلية، لأن \(u_k\equiv1\) يعني \(k^2\equiv0\pmod{M}\)، وهذا مستحيل عندما \(1\le k<M\). ومع ذلك تحتفظ التطبيقات بهذا الفرع كي لا يحدث أي تقسيم على الصفر في الحسابات المعيارية.

مثال محلول: \(n=4\)

لنحسب الآن قيمة التحقق مباشرة.

عند \(k=1\) نجد \(u_1=0\)، ومن ثم

$$T_1=0+0+0+0=0.$$

وعند \(k=2\) يكون \(u_2=-3\)، لذا

$$T_2=(-3)+(-3)^2+(-3)^3+(-3)^4=-3+9-27+81=60.$$

وعند \(k=3\) نحصل على \(u_3=-8\)، وبالتالي

$$T_3=-8+64-512+4096=3640.$$

وعند \(k=4\) يكون \(u_4=-15\)، فينتج

$$T_4=-15+225-3375+50625=47460.$$

وبجمع هذه المساهمات كلها نحصل على

$$S(4)=0+60+3640+47460=51160,$$

وهو تمامًا مقدار التحقق الموجود في التنفيذ.

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

تتبع تطبيقات C++ وPython وJava البنية الرياضية نفسها. فهي تمر مرة واحدة على \(k=1,2,\dots,n\)، ثم تحوّل المقدار \(1-k^2\) إلى باقي معياري بين \(0\) و\(M-1\)، وبعد ذلك تحسب مساهمة \(T_k\) من الصيغة المغلقة.

لكل \(k\)، تحسب الشيفرة أولًا \(u_k^n \bmod M\) باستخدام الأسّ الثنائي السريع. وإذا كان المقام غير صفري، تحسب معكوسه الضربي برفعه إلى القوة \(M-2\)، أيضًا بالأسّ السريع. بعد ذلك تُضاف المساهمة الحالية إلى الجواب الكلي مع أخذ المودولو في كل خطوة.

ويحتوي أحد التطبيقات كذلك على تحققين صغيرين: التأكد من أن \(S(4)=51160\)، ومقارنة الصيغة المحسّنة مع الجمع المباشر على إدخال صغير. وهذا يثبت أن الاختزال الجبري نُفِّذ بصورة صحيحة.

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

هناك حلقة خارجية واحدة فقط على \(k\) من \(1\) إلى \(n\). ولكل قيمة من \(k\)، تُنفَّذ كمية ثابتة من العمليات المعيارية، بالإضافة إلى أسّ سريع لحساب \(u_k^n\)، وأسّ سريع آخر لحساب المعكوس. لذلك يكون الزمن الكلي

$$O\bigl(n(\log n+\log M)\bigr).$$

ومع ثبات المودولو في المسألة عند \(M=10^9+7\)، يمكن النظر إلى ذلك عمليًا على أنه

$$O(n\log n).$$

أما الذاكرة فهي

$$O(1),$$

لأن البرنامج يحتفظ بعدد قليل فقط من القيم الحالية. وهذا أفضل بكثير من الطريقة المباشرة ذات الكلفة \(O(n^2)\).

حواشٍ ومراجع

  1. صفحة المسألة: https://projecteuler.net/problem=479
  2. المتتالية الهندسية: Wikipedia — متتالية هندسية
  3. مبرهنة فيرما الصغرى: Wikipedia — مبرهنة فيرما الصغرى
  4. الأسّ المعياري: Wikipedia — Modular exponentiation