2017-12-30 27 views
-2

Cで巨大な数値の最大の素因数を見つけようとしていますが、100や10000のような小さな数値に対してはうまく動作しますが失敗します。私のcore2duoとi5)の数十分間、非常に大きいtargetの番号(対象番号のコードを参照してください) アルゴリズムは正しいですか?Cコードは永遠に実行し続けます*

私はCの新人であり、大きな数字で本当に苦労しています。私が欲しいのは、訂正やガイダンスは解決策ではありません。私はbignumバインディングやものでpythonを使ってこれを行うことができます(私はまだ試していませんが、確かにです)。あるいは、私はいくつかの小さな間違いをしているかもしれません実現するにはあまりにも疲れて、とにかくここに私が書いたコードです:

#include <stdio.h> 
// To find largest prime factor of target 
int is_prime(unsigned long long int num); 

long int main(void) { 
    unsigned long long int target = 600851475143; 
    unsigned long long int current_factor = 1; 
    register unsigned long long int i = 2; 
    while (i < target) { 
     if ((target % i) == 0 && is_prime(i) && (i > current_factor)) { //verify i as a prime factor and greater than last factor 
      current_factor = i; 
     } 
     i++; 
    } 
    printf("The greates is: %llu \n",current_factor); 
return(0); 
} 


int is_prime (unsigned long long int num) { //if num is prime 1 else 0 
    unsigned long long int z = 2; 
    while (num > z && z !=num) { 
     if ((num % z) == 0) {return 0;} 
     z++; 
    } 

return 1; 
} 
+1

は今までそれが現在ある場所を確認するためにデバッガで立ち止まっ? 文脈のないコードを読んでいると仮定すると、is_primeはオーバーフローのためにループがハングアップするかもしれません。 – X39

+0

いいえ、私はしませんでしたが、コードが正常に機能しているかどうかを知るためにprintfを追加しました。しかし、どれくらいの期間ですか? –

+1

'is_prime'を半分に最適化する:偶数をテストし、次に' z = 3'を設定し、ループの繰り返しごとに2でインクリメントします。 –

答えて

4

6000億回の繰り返しは、ほんのわずかな時間がかかります。これを実質的に減らす必要があります。

ヒント:任意の整数値xが与えられた場合、yが要因であることが判明した場合、x/yも要因であることが暗黙的に検出されています。つまり、要素は常にペアになります。したがって、冗長な作業を行う前にどのくらい反復する必要があるかには限界があります。

この制限は何ですか?さて、yx/yより大きくなるクロスオーバーポイントは何ですか?

この最適化を外側ループに適用すると、コードのランタイムはis_prime関数によって制限されることがわかります。もちろん、同様の手法を適用することもできます。

+0

あなたが私に与えてくれた解決策が正しいと確信できたら、おそらく私はSEを残して自分自身でそれを理解しなければならないかもしれないので、あなたはとても確信していますか? –

+1

@UbdusSamad - はい。私が「読者のための練習として残した」ビットは、その限界が何であるかを理解することです。 –

+0

ありがとう、私はそれに取り組んで、2時間も私の最初のアプローチを実行してみてください。再度、感謝します! –

4

の数の平方根になるまで反復することにより、我々はそれの要因のすべてを取得することができます(factorN/factorfactor<=sqrt(N))。この小さなアイデアのもとで、解決策が存在します。我々がチェックするsqrt(N)よりも小さい因子は、対応する因子がsqrt(N)より大きい。だから、sqrt(N)までチェックするだけで、残りの要素を得ることができます。

ここでは、プライム検索アルゴリズムを明示的に使用する必要はありません。因数分解ロジック自体は、ターゲットが素数であるかどうかを推論します。したがって、ペアワイズ要因を確認するだけです。

unsigned long long ans ; 
for(unsigned long long i = 2; i<=target/i; i++) 
    while(target % i == 0){ 
     ans = i; 
     target/=i; 
    } 

if(target > 1) ans = target; // that means target is a prime. 
//print ans 

編集:追加する点(chux) - 以前のコードでi*iは、我々はi<=target/iを使用する場合、回避することができるオーバーフローにつながる可能性があります。整数演算とダブル数学の混合が丸め誤差を起こしやすい -

また別の選択肢がここ

unsigned long long sqaure_root = isqrt(target); 
for(unsigned long long i = 2; i<=square_root; i++){ 
... 
} 

を持っているだろうが以来sqrtの使用は賢明な選択ではないよりも注意してください。

答えが6857の場合、ターゲットに与えられます。

+0

注: 'i * i 'はオーバーフローする可能性があります。 'for(unsigned long long i = 2; i <= target/i; i ++)'はできません。 'target/i'のコストは、' target%i'を見て、 'target%i'と' target/i'を一緒に計算する良いコンパイルで、しばしば最小です。 – chux

+0

@chux: 'i <= sqrt(target)'または 'i <= target/i'が良い選択でしょうか? – coderredoc

+0

'sqrt(target)'は悪い選択です。 「二重」演算と整数演算を混在させると、丸め誤差が発生しやすくなります。 'sqrt(target)'を再計算すると、各ループも弱くなります。コードはループの前に以前の 'unsigned long long sqtarget = isqrt(target);'を実行することができます。 – chux

0

is_primeは、numを他の素数に対してのみテストする必要があるという点で、鶏と卵の問題があります。したがって、それは3の倍数であるため、チェックする必要はありません。

is_primeは素数の配列を維持することができ、新しいnumがテストされるたびにそれが配列に追加されます。 num isrは配列の各素数に対してテストされ、配列内のいずれかの素数で割り切れない場合は、それ自身が素数であり、配列に追加されます。あなたがターゲットとする素数の数を計算する形式がない限り、arayはmallocされ、rellocされなければなりません(私はそのような公式は存在しないと信じています)。

EDIT:ターゲット600,851,475,143をテストするための素数の数は約7,500,000,000になり、テーブルがメモリ不足になる可能性があります。

次のようなアプローチを適合させることができる。

  • 上にブルートフォースを使用すること

  • 上方素数ためunsigned long long intを使用するUINT_max

  • の素数までunsiged intを使用します特定のメモリ消費。

UINT_MAX 4,294,967,295として定義され、周りの千億まで素数をカバーする7.5 * 4 = 30GB

を要するであろうもThe Prime Pagesを参照してください。

+0

...理由を述べないdownvotesは禁止されるべきである...アプローチに間違って何も見ることができない。説明してください。 –

+0

その配列はメモリを消費し、あまりにも多くのメモリを消費します! –

+1

'is_prime'を最適化することは、一度だけしか呼ばれていないので不要です。あるいは、外側のループが最適化されるまで最適化する必要はありません。 –

0

何もそれだけで例えば、最適化を必要とする、間違ったではありません:(Z < NUM)ながら

int is_prime(unsigned long long int num) { 
    if (num == 2) { 
     return (1);    /* Special case */ 
    } 
    if (num % 2 == 0 || num <= 1) { 
     return (0); 
    } 
    unsigned long long int z = 3; /* We skipped the all even numbers */ 
    while (z < num) {    /* Do a single test instead of your redundant ones */ 
     if ((num % z) == 0) { 
      return 0; 
     } 
     z += 2;      /* Here we go twice as fast */ 
    } 
return 1; 
} 

も大きな他の問題があるが、あなたが解決策を望んでいないので、私はあなたがどのように見つけてみましょうそれを最適化し、同様に最初の関数を自分自身で調べます。

EDIT:私の前に50秒前に投稿された素数配列のリストが最高ですが、初心者で操作が簡単ではないので、簡単なソリューションを提供することにしました。ポインターやものを学ぶ)。

+0

downvotesありがとう...私はそれが最適ではない解決策のためだと思うが、OPに彼は_learning_ Cとこれは簡単に理解できると述べた。 –

+0

私は配列とポインタをうまく処理できます! –

+0

ああ、右揃えでは、動的に割り当てられたlong long intの配列を見ている間に見つかった素数を追加する必要があります。どれくらいのRAMがあるのか​​分かりませんが、ローエンドのラップトップではいくつかの問題が発生する可能性があります例...(実際にはあなたのシステムのsizeof(long long int)に依存しますが、私は小さなPCでは16ではないので実際の問題はありません) –

2

コードは、2つの主要な問題

  1. while (i < target)ループは非常に非効率的であるがあります。因数を見つけると、targettarget = target/i;に減らすことができました。さらに、因子iが複数回現れる可能性がある。修正は表示されません。

  2. is_prime(n)は非常に非効率的です。そのwhile (num > z && z !=num)はループすることができますn時間。ここでも、商を使用して反復をsqrt(n)回に制限します。

    int is_prime (unsigned long long int num) { 
        unsigned long long int z = 2; 
        while (z <= num/z) { 
        if ((num % z) == 0) return 0; 
        z++; 
        } 
        return num > 1; 
    } 
    
関連する問題