質問

N が大きい場合、 1 < k < N となるようにすべての phi(k) を反復処理する必要があります。

  • 時間計算量は次のとおりである必要があります O(N logN)
  • メモリ複雑度はサブである必要があります O(N) (N の値は 10 程度になるため、12)

出来ますか?もしそうなら、どのようにして?

役に立ちましたか?

解決

これは、以下のコード例で実装されているように、メモリの複雑さO(Sqrt(N))とCPUの複雑さO(N * Log(Log(N)))で最適化されたウィンドウエラトステネスのふるいで実行できます。

言語が指定されておらず、Pythonを知らないため、VB.netに実装しましたが、必要に応じてC#に変換できます。

Imports System.Math

Public Class TotientSerialCalculator
    'Implements an extremely efficient Serial Totient(phi) calculator   '
    '  This implements an optimized windowed Sieve of Eratosthenes.  The'
    ' window size is set at Sqrt(N) both to optimize collecting and     '
    ' applying all of the Primes below Sqrt(N), and to minimize         '
    ' window-turning overhead.                                          '
    '                                                                   '
    ' CPU complexity is O( N * Log(Log(N)) ), which is virtually linear.'
    '                                                                   '
    ' MEM Complexity is O( Sqrt(N) ).                                   '
    '                                                                   '
    ' This is probalby the ideal combination, as any attempt to further '
    'reduce memory will almost certainly result in disproportionate increases'
    'in CPU complexity, and vice-versa.                                 '

    Structure NumberFactors
        Dim UnFactored As Long  'the part of the number that still needs to be factored'
        Dim Phi As Long 'the totient value progressively calculated'
        '               (equals total numbers less than N that are CoPrime to N)'
        'MEM = 8 bytes each'
    End Structure

    Private ReportInterval As Long
    Private PrevLast As Long     'the last value in the previous window'
    Private FirstValue As Long   'the first value in this windows range'
    Private WindowSize As Long
    Private LastValue As Long    'the last value in this windows range'
    Private NextFirst As Long    'the first value in the next window'

    'Array that stores all of the NumberFactors in the current window.'
    ' this is the primary memory consumption for the class and it'
    ' is 16 * Sqrt(N) Bytes, which is O(Sqrt(N)).'
    Public Numbers() As NumberFactors
    ' For N=10^12 (1 trilion), this will be 16MB, which should be bearable anywhere.'
    '(note that the Primes() array is a secondary memory consumer'
    '  at O(pi(Sqrt(N)), which will be within 10x of O(Sqrt(N)))'

    Public Event EmitTotientPair(ByVal k As Long, ByVal Phi As Long)

    '===== The Routine To Call: ========================'
    Public Sub EmitTotientPairsToN(ByVal N As Long)
        'Routine to Emit Totient pairs {k, Phi(k)} for k = 1 to N'
        '   2009-07-14, RBarryYoung, Created.'
        Dim i As Long
        Dim k As Long   'the current number being factored'
        Dim p As Long   'the current prime factor'

        'Establish the Window frame:'
        '   note: WindowSize is the critical value that controls both memory'
        '    usage and CPU consumption and must be SQRT(N) for it to work optimally.'
        WindowSize = Ceiling(Sqrt(CDbl(N)))
        ReDim Numbers(0 To WindowSize - 1)

        'Initialize the first window:'
        MapWindow(1)
        Dim IsFirstWindow As Boolean = True

        'adjust this to control how often results are show'
        ReportInterval = N / 100

        'Allocate the primes array to hold the primes list:'
        '  Only primes <= SQRT(N) are needed for factoring'
        '  PiMax(X) is a Max estimate of the number of primes <= X'
        Dim Primes() As Long, PrimeIndex As Long, NextPrime As Long
        'init the primes list and its pointers'
        ReDim Primes(0 To PiMax(WindowSize) - 1)
        Primes(0) = 2   '"prime" the primes list with the first prime'
        NextPrime = 1

        'Map (and Remap) the window with Sqrt(N) numbers, Sqrt(N) times to'
        ' sequentially map all of the numbers <= N.'
        Do
            'Sieve the primes across the current window'
            PrimeIndex = 0
            'note: cant use enumerator for the loop below because NextPrime'
            ' changes during the first window as new primes <= SQRT(N) are accumulated'
            Do While PrimeIndex < NextPrime
                'get the next prime in the list'
                p = Primes(PrimeIndex)
                'find the first multiple of (p) in the current window range'
                k = PrevLast + p - (PrevLast Mod p)

                Do
                    With Numbers(k - FirstValue)
                        .UnFactored = .UnFactored \ p   'always works the first time'
                        .Phi = .Phi * (p - 1)           'Phi = PRODUCT( (Pi-1)*Pi^(Ei-1) )'
                        'The loop test that follows is probably the central CPU overhead'
                        ' I believe that it is O(N*Log(Log(N)), which is virtually O(N)'
                        ' ( for instance at N = 10^12, Log(Log(N)) = 3.3 )'
                        Do While (.UnFactored Mod p) = 0
                            .UnFactored = .UnFactored \ p
                            .Phi = .Phi * p
                        Loop
                    End With

                    'skip ahead to the next multiple of p: '
                    '(this is what makes it so fast, never have to try prime factors that dont apply)'
                    k += p
                    'repeat until we step out of the current window:'
                Loop While k < NextFirst

                'if this is the first window, then scan ahead for primes'
                If IsFirstWindow Then
                    For i = Primes(NextPrime - 1) + 1 To p ^ 2 - 1  'the range of possible new primes'
                        'Dont go beyond the first window'
                        If i >= WindowSize Then Exit For
                        If Numbers(i - FirstValue).UnFactored = i Then
                            'this is a prime less than SQRT(N), so add it to the list.'
                            Primes(NextPrime) = i
                            NextPrime += 1
                        End If
                    Next
                End If

                PrimeIndex += 1     'move to the next prime'
            Loop

            'Now Finish & Emit each one'
            For k = FirstValue To LastValue
                With Numbers(k - FirstValue)
                    'Primes larger than Sqrt(N) will not be finished: '
                    If .UnFactored > 1 Then
                        'Not done factoring, must be an large prime factor remaining: '
                        .Phi = .Phi * (.UnFactored - 1)
                        .UnFactored = 1
                    End If

                    'Emit the value pair: (k, Phi(k)) '
                    EmitPhi(k, .Phi)
                End With
            Next

            're-Map to the next window '
            IsFirstWindow = False
            MapWindow(NextFirst)
        Loop While FirstValue <= N
    End Sub

    Sub EmitPhi(ByVal k As Long, ByVal Phi As Long)
        'just a placeholder for now, that raises an event to the display form' 
        ' periodically for reporting purposes.  Change this to do the actual'
        ' emitting.'
        If (k Mod ReportInterval) = 0 Then
            RaiseEvent EmitTotientPair(k, Phi)
        End If
    End Sub

    Public Sub MapWindow(ByVal FirstVal As Long)
        'Efficiently reset the window so that we do not have to re-allocate it.'

        'init all of the boundary values'
        FirstValue = FirstVal
        PrevLast = FirstValue - 1
        NextFirst = FirstValue + WindowSize
        LastValue = NextFirst - 1

        'Initialize the Numbers prime factor arrays'
        Dim i As Long
        For i = 0 To WindowSize - 1
            With Numbers(i)
                .UnFactored = i + FirstValue 'initially equal to the number itself'
                .Phi = 1        'starts at mulplicative identity(1)'
            End With
        Next
    End Sub

    Function PiMax(ByVal x As Long) As Long
        'estimate of pi(n) == {primes <= (n)} that is never less'
        ' than the actual number of primes. (from P. Dusart, 1999)'
        Return (x / Log(x)) * (1.0 + 1.2762 / Log(x))
    End Function
End Class

O(N * Log(Log(N)))で、このルーチンは平均でO(Log(Log(N)))の各数値を因数分解することに注意してください。ここでいくつかの返信によってサイトに配置されたアルゴリズム。実際、N = 10 ^ 12で 2400 倍高速です!

このルーチンを2Ghz Intel Core 2ラップトップでテストしましたが、1秒間に3,000,000以上のPhi()値を計算します。この速度では、10 ^ 12の値を計算するのに約4日かかります。また、エラーなしで最大100,000,000まで正確性をテストしました。 64ビット整数に基づいているため、2 ^ 63(10 ^ 19)までは正確でなければなりません(ただし、誰にとっても遅すぎます)。

また、実行/テスト用のVisual Studio WinForm(VB.net)もあります。必要に応じて提供できます。

質問がある場合は教えてください。


コメントで要求されているように、C#バージョンのコードの下に追加しました。ただし、現在は他のプロジェクトの途中にいるため、自分で変換する時間がないため、オンラインVBからC#への変換サイトの1つを使用しました( http://www.carlosag.net/tools/codetranslator/ )。そのため、これは自動変換されたものであり、まだ自分でテストまたはチェックする時間がないことに注意してください。

using System.Math;
public class TotientSerialCalculator {

    // Implements an extremely efficient Serial Totient(phi) calculator   '
    //   This implements an optimized windowed Sieve of Eratosthenes.  The'
    //  window size is set at Sqrt(N) both to optimize collecting and     '
    //  applying all of the Primes below Sqrt(N), and to minimize         '
    //  window-turning overhead.                                          '
    //                                                                    '
    //  CPU complexity is O( N * Log(Log(N)) ), which is virtually linear.'
    //                                                                    '
    //  MEM Complexity is O( Sqrt(N) ).                                   '
    //                                                                    '
    //  This is probalby the ideal combination, as any attempt to further '
    // reduce memory will almost certainly result in disproportionate increases'
    // in CPU complexity, and vice-versa.                                 '
    struct NumberFactors {

        private long UnFactored;  // the part of the number that still needs to be factored'
        private long Phi;
    }

    private long ReportInterval;
    private long PrevLast;       // the last value in the previous window'
    private long FirstValue;     // the first value in this windows range'
    private long WindowSize;
    private long LastValue;      // the last value in this windows range'
    private long NextFirst;      // the first value in the next window'

    // Array that stores all of the NumberFactors in the current window.'
    //  this is the primary memory consumption for the class and it'
    //  is 16 * Sqrt(N) Bytes, which is O(Sqrt(N)).'
    public NumberFactors[] Numbers;
    //  For N=10^12 (1 trilion), this will be 16MB, which should be bearable anywhere.'
    // (note that the Primes() array is a secondary memory consumer'
    //   at O(pi(Sqrt(N)), which will be within 10x of O(Sqrt(N)))'

//NOTE: this part looks like it did not convert correctly
    public event EventHandler EmitTotientPair;
    private long k;
    private long Phi;

    // ===== The Routine To Call: ========================'
    public void EmitTotientPairsToN(long N) {
        // Routine to Emit Totient pairs {k, Phi(k)} for k = 1 to N'
        //    2009-07-14, RBarryYoung, Created.'
        long i;
        long k;
        // the current number being factored'
        long p;
        // the current prime factor'
        // Establish the Window frame:'
        //    note: WindowSize is the critical value that controls both memory'
        //     usage and CPU consumption and must be SQRT(N) for it to work optimally.'
        WindowSize = Ceiling(Sqrt(double.Parse(N)));
        object Numbers;
        this.MapWindow(1);
        bool IsFirstWindow = true;
        ReportInterval = (N / 100);
        // Allocate the primes array to hold the primes list:'
        //   Only primes <= SQRT(N) are needed for factoring'
        //   PiMax(X) is a Max estimate of the number of primes <= X'
        long[] Primes;
        long PrimeIndex;
        long NextPrime;
        // init the primes list and its pointers'
        object Primes;
        -1;
        Primes[0] = 2;
        // "prime" the primes list with the first prime'
        NextPrime = 1;
        // Map (and Remap) the window with Sqrt(N) numbers, Sqrt(N) times to'
        //  sequentially map all of the numbers <= N.'
        for (
        ; (FirstValue <= N); 
        ) {
            PrimeIndex = 0;
            // note: cant use enumerator for the loop below because NextPrime'
            //  changes during the first window as new primes <= SQRT(N) are accumulated'
            while ((PrimeIndex < NextPrime)) {
                // get the next prime in the list'
                p = Primes[PrimeIndex];
                // find the first multiple of (p) in the current window range'
                k = (PrevLast 
                            + (p 
                            - (PrevLast % p)));
                for (
                ; (k < NextFirst); 
                ) {
                    // With...
                    UnFactored;
                    p;
                    // always works the first time'
                    (Phi 
                                * (p - 1));
                    while (// TODO: Warning!!!! NULL EXPRESSION DETECTED...
                    ) {
                        (UnFactored % p);
                        UnFactored;
                        (Phi * p);
                    }

                    // skip ahead to the next multiple of p: '
                    // (this is what makes it so fast, never have to try prime factors that dont apply)'
                    k = (k + p);
                    // repeat until we step out of the current window:'
                }

                // if this is the first window, then scan ahead for primes'
                if (IsFirstWindow) {
                    for (i = (Primes[(NextPrime - 1)] + 1); (i 
                                <= (p | (2 - 1))); i++) {
                        // the range of possible new primes'
                        // TODO: Warning!!! The operator should be an XOR ^ instead of an OR, but not available in CodeDOM
                        // Dont go beyond the first window'
                        if ((i >= WindowSize)) {
                            break;
                        }

                        if ((Numbers[(i - FirstValue)].UnFactored == i)) {
                            // this is a prime less than SQRT(N), so add it to the list.'
                            Primes[NextPrime] = i;
                            NextPrime++;
                        }

                    }

                }

                PrimeIndex++;
                // move to the next prime'
            }

            // Now Finish & Emit each one'
            for (k = FirstValue; (k <= LastValue); k++) {
                // With...
                // Primes larger than Sqrt(N) will not be finished: '
                if ((Numbers[(k - FirstValue)].UnFactored > 1)) {
                    // Not done factoring, must be an large prime factor remaining: '
                    (Numbers[(k - FirstValue)].Phi * (Numbers[(k - FirstValue)].UnFactored - 1).UnFactored) = 1;
                    Numbers[(k - FirstValue)].Phi = 1;
                }

                // Emit the value pair: (k, Phi(k)) '
                this.EmitPhi(k, Numbers[(k - FirstValue)].Phi);
            }

            // re-Map to the next window '
            IsFirstWindow = false;
            this.MapWindow(NextFirst);
        }

    }

    void EmitPhi(long k, long Phi) {
        // just a placeholder for now, that raises an event to the display form' 
        //  periodically for reporting purposes.  Change this to do the actual'
        //  emitting.'
        if (((k % ReportInterval) 
                    == 0)) {
            EmitTotientPair(k, Phi);
        }

    }

    public void MapWindow(long FirstVal) {
        // Efficiently reset the window so that we do not have to re-allocate it.'
        // init all of the boundary values'
        FirstValue = FirstVal;
        PrevLast = (FirstValue - 1);
        NextFirst = (FirstValue + WindowSize);
        LastValue = (NextFirst - 1);
        // Initialize the Numbers prime factor arrays'
        long i;
        for (i = 0; (i 
                    <= (WindowSize - 1)); i++) {
            // With...
            // initially equal to the number itself'
            Phi = 1;
            // starts at mulplicative identity(1)'
        }

    }

    long PiMax(long x) {
        // estimate of pi(n) == {primes <= (n)} that is never less'
        //  than the actual number of primes. (from P. Dusart, 1999)'
        return ((x / Log(x)) * (1 + (1.2762 / Log(x))));
    }
}

他のヒント

phi(k)をより高速に計算する方法を見つけた人はいません(別名、 Eulerのtotient関数)最初にkの素因数を見つけることより。この問題を解決するより速い方法を見つけると RSA公開鍵アルゴリズム。 (RSAの公開鍵と秘密鍵は両方ともphi(n)に基づいて計算されます。nは2つの大きな素数の積です。)

phi(k) の計算は、k の素因数分解を使用して実行する必要があります。これが唯一の賢明な方法です。それについて復習が必要な場合は、 ウィキペディアには公式が掲載されています.

ここで、1 から大きな N までのすべての数値のすべての素約数を計算しなければならない場合、結果を見る前に老衰で死んでしまうでしょう。したがって、私は逆のことをします。N 以下のすべての数値を、考えられる素因数を使用して構築します。N 以下のすべての素数。

したがって、問題は数値のすべての約数を計算することに似ていますが、因数分解で特定の素数を見つけることができる最大回数が事前にわからないだけです。元々 Tim Peters が Python リストで書いたイテレーターを微調整する (何か) についてブログに書きました...) totient 関数を含めるには、k, phi(k) ペアを生成する Python での可能な実装は次のようになります。

def composites(factors, N) :
    """
    Generates all number-totient pairs below N, unordered, from the prime factors.
    """
    ps = sorted(set(factors))
    omega = len(ps)

    def rec_gen(n = 0) :
        if n == omega :
            yield (1,1)
        else :
            pows = [(1,1)]
            val = ps[n]
            while val <= N :
                pows += [(val, val - pows[-1][0])]
                val *= ps[n]
            for q, phi_q in rec_gen(n + 1) :
                for p, phi_p in pows :
                    if p * q > N :
                        break
                    else :
                        yield p * q, phi_p * phi_q

    for p in rec_gen() :
        yield p

N より下のすべての素因数の計算についてヘルプが必要な場合は、次のリンクを参照してください。 それについてブログに書いた...ただし、10 未満の素数はすべて計算されることに注意してください。12 それ自体は非常に注目に値する偉業です...

これはProject Euler 245によるものですか?私はその質問を覚えていて、それをあきらめました。

トーティエントを計算するために見つけた最速の方法は、kに反復因子がないことを考えると、素因数(p-1)を乗算することでした(問題を正しく覚えている場合はそうではありませんでした)。

したがって、係数の計算には、 gmpy.next_prime または pyecm (楕円曲線因子分解)。

Jaimeが示唆するように、素因数をふるいにかけることもできます。 10 12 までの数値の場合、最大素因数は100万未満であり、これは妥当な値です。

因数分解を覚えておくと、phi関数をさらに高速化できます。

この種の問題には、整数ごとに返す反復子を使用しています m&lt; N 素数のリスト&lt; mを分割するsqrt( N )このようなイテレータを実装するには、長さ R の配列 A を使用します。ここで R &gt; sqrt( N )。各ポイントで、配列 A には、整数 m .. m + R -1を分割する素数のリストが含まれています。つまり A [ m R ]には、 m を分割する素数が含まれています。各素数 p は正確に1つのリストにあります。つまり、 A [ m R ]範囲 m .. m + R -1で、 p で割り切れます。イテレータの次の要素を生成するとき、単に A [ m R ]のリストが返されます。次に、素数のリストが A [ m R ]から削除され、各素数pが A に追加されます[( m + p )%R]。

素数のリスト&lt; sqrt( N )を m で除算すると、 m の素因数分解を見つけるのは簡単です。sqrt( N )。

このメソッドの複雑さはO( N log(log( N )))です。リスト操作を含むすべての操作がO(1)を取ると仮定しています。メモリ要件はO(sqrt( N ))です。

残念ながら、ここには一定のオーバーヘッドがあります。したがって、phi(n)の値を生成するよりエレガントな方法を探していましたが、成功していませんでした。

これは効率的なpythonジェネレーターです。注意点は、順番に結果が得られないことです。 https://stackoverflow.com/a/10110008/412529 に基づいています。

一度に1つの数値の素因数のリストを保存するだけでよいため、メモリの複雑さはO(log(N))です。

CPUの複雑さは、O(N log log N)のような超線形にすぎません。

def totientsbelow(N):
    allprimes = primesbelow(N+1)
    def rec(n, partialtot=1, min_p = 0):
        for p in allprimes:
            if p > n:
                break
            # avoid double solutions such as (6, [2,3]), and (6, [3,2])
            if p < min_p: continue
            yield (p, p-1, [p])
            for t, tot2, r in rec(n//p, partialtot, min_p = p): # uses integer division
                yield (t*p, tot2 * p if p == r[0] else tot2 * (p-1), [p] + r)

    for n, t, factors in rec(N):
        yield (n, t)

私はあなたが反対に行くことができると思います。整数kを因数分解してphi(k)を取得する代わりに、素数から1からNまでのすべての整数を生成し、phi(k)をすばやく取得しようとすることができます。たとえば、P n がN未満の最大素数の場合、N未満のすべての整数を生成できます

P 1 i 1 * P 2 i 2 * ... * P n i n ここで、各i j は0から[ log(N)/ log(P j )]([]はフロア関数です)。

そのようにして、高価な素因数分解なしでphi(k)をすばやく取得し、1からNまでのすべてのkを繰り返し処理できます(順序はありませんが、順序は気にしないと思います)。

n までの報酬をふるいにかける:

(define (totients n)
  (let ((tots (make-vector (+ n 1))))
    (do ((i 0 (+ i 1))) ((< n i))
      (vector-set! tots i i))
    (do ((i 2 (+ i 1))) ((< n i) tots)
      (when (= i (vector-ref tots i))
        (vector-set! tots i (- i 1))
        (do ((j (+ i i) (+ i j))) ((< n j))
          (vector-set! tots j
            (* (vector-ref tots j) (- 1 (/ i)))))))))

これは、N = PQを因数分解します。ここで、P&amp; Qは素数です。

ElixirまたはErlangで非常にうまく機能します。

擬似乱数シーケンスにさまざまなジェネレーターを試すことができます。 x * x + 1 が一般的に使用されます。

この行: defp f0(x、n)、do:rem((x * x)+ 1、n)

その他の改善点:より良いまたは代替の gcd rem および abs 関数

defmodule Factorizer do

  def factorize(n) do
    t = System.system_time

    x = pollard(n, 2_000_000, 2_000_000)
    y = div(n, x)
    p = min(x, y)
    q = max(x, y)

    t = System.system_time - t

    IO.puts "
Factorized #{n}: into [#{p} , #{q}] in #{t} μs
"

    {p, q}
  end

  defp gcd(a,0), do: a
  defp gcd(a,b), do: gcd(b,rem(a,b))

  defp pollard(n, a, b) do
    a = f0(a, n)
    b = f0(f0(b, n), n)

    p = gcd(abs(b - a), n)

    case p > 1 do
      true  -> p
      false -> pollard(n, a, b)
    end
  end

  defp f0(x, n), do: rem((x * x) + 1, n)

end
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top