トリッキーなGoogleインタビューの質問


169

私の友人が就職の面接をしています。インタビューの質問の1つが私に考えさせられました。

2つの負でない整数があります:iとj。次の方程式が与えられた場合、出力がソートされるようにiとjを反復する(最適な)解を見つけます。

2^i * 5^j

したがって、最初の数ラウンドは次のようになります。

2^0 * 5^0 = 1
2^1 * 5^0 = 2
2^2 * 5^0 = 4
2^0 * 5^1 = 5
2^3 * 5^0 = 8
2^1 * 5^1 = 10
2^4 * 5^0 = 16
2^2 * 5^1 = 20
2^0 * 5^2 = 25

どうやら、パターンが見えません。あなたの考え?


63
プログラマー時間の観点からの最適なアルゴリズムは、2つのネストされたループで生成してからソートすることです。なぜ彼らはこのような質問をするのですか?
Tom Zych、2011年

21
どちらが大きいかを調べることで、遷移ポイントを判別できる場合があります。2^2 < 5しかし2^3 > 5、その時点でjを増やします。O(nlgn)ではなくO(n)で出力を生成できると思います。@ tom-zynch 2つのネストされたループはO(n ^ 2)です。この質問は非常に有効です
ミハイル

1
出力は1つしかないため、最適な解はO(n)です。以下の私のソリューションをお読みください
ミハイル

3
同様の質問が以前に明らかに対処されています:stackoverflow.com/questions/4600048/nth-ugly-number

1
...そしてOPはおそらくすでに答えを選択するはずです。結局のところ、彼はすでに良いものをたくさん持っています。
アベル

回答:


123

ダイクストラは、「プログラミングの分野」で雄弁な解決策を導き出します。彼は問題をハミングに帰します。これがダイクストラのソリューションの私の実装です。

int main()
{
    const int n = 20;       // Generate the first n numbers

    std::vector<int> v(n);
    v[0] = 1;

    int i2 = 0;             // Index for 2
    int i5 = 0;             // Index for 5

    int x2 = 2 * v[i2];     // Next two candidates
    int x5 = 5 * v[i5];

    for (int i = 1; i != n; ++i)
    {
        int m = std::min(x2, x5);
        std::cout << m << " ";
        v[i] = m;

        if (x2 == m)
        {
            ++i2;
            x2 = 2 * v[i2];
        }
        if (x5 == m)
        {
            ++i5;
            x5 = 5 * v[i5];
        }
    }

    std::cout << std::endl;
    return 0;
}

18
関連リンク:en.wikipedia.org/wiki/Regular_number#Algorithms。ちなみに、これは非常に良いインタビューの質問ではないと思います。これは、ダイクストラによる(手書きの論文)であり、この問題のアルゴリズムを提供して証明しています。cs.utexas.edu
Ebbing

目標が「iとjを反復する」場合、必要なストレージ容量は少なく、FIFOで十分です。私のPythonソリューションを参照してください。
GaBorgulya 2011

7
目標が「iとjを反復する」場合は、同じ問題ではありません。
ムム

これは、最小限のメモリを使用する、本当に素晴らしい実装です。ただし、1つの数値だけが必要な場合でも、線形メモリです。
Thomas Ahle

1
@ThomasAhle これを見たかどうかわかりませんが、n番目の数値を個別に計算できるコードが最後にあります。例えば10億分の1のように。
ネスは

47

これは、より洗練された方法です(以前の回答よりも洗練されています)。

数が行列に配置されていると想像してください:

     0    1    2    3    4    5   -- this is i
----------------------------------------------
0|   1    2    4    8   16   32
1|   5   10   20   40   80  160
2|  25   50  100  200  400  800
3| 125  250  500 1000 2000 ...
4| 625 1250 2500 5000 ...
j on the vertical

あなたがしなければならないことは、から始まるこの行列を「歩く」こと(0,0)です。また、次の動きの可能性を追跡する必要があります。初め(0,0)は2つのオプションしかありません。(0,1)または(1,0):の値(0,1)が小さいため、それを選択します。その後、あなたの次の選択のために同じことをします(0,2)(1,0)。これまでのところ、次のリストがあります1, 2, 4。次の動きは(1,0)、そこでの値がより小さいため(0,3)です。ただし、次の移動には、、または、またはの3つの選択肢があります。(0,3)(1,1)(2,0)

リストを取得するためにマトリックスは必要ありませんが、すべての選択肢を追跡する必要があります(つまり、125以上に到達すると、4つの選択肢があります)。


同じように考えていたのでこれに投票しましたが、一般的にはO(i ^ 2 * j)のようなものではないでしょうか。出力する数値ごとにいくつかの数値を確認する必要があります。
Tom Zych、2011年

1
@Tomでは、複数の数値をチェックする必要がありますが、それほど悪くはありません。125から625までの数値を出力する場合は、4つの値を調べる必要があります。625と3025の間で、5つの値を調べます。本当に、j1つの出力ごとにチェックされます
vlad

+1:この質問と組み合わせてください:stackoverflow.com/questions/5000836/search-algorithmそして、O(n)ソリューションがあるように見えます。

@Moronよろしくお願いします。そのアルゴリズムに25ドルは払いたくないのですが、興味深いようです。
vlad

1
実際にj ~ n^0.5は、n値がi x j平面上の領域を埋めるため、シーケンスのn番目の値の場合。つまり、このアルゴはO(n^1.5)時間のある、O(n^0.5)空間です。しかし、同じ空間complxtyの線形時間アルゴが存在し、n^0.5以下の答えからのミニヒープアルゴはO(n*log(n))同じn^0.5空間の時間です。
Will Ness

25

最小ヒープを使用します。

置く1。

抽出分。xを取得するとします。

2xと5xをヒープにプッシュします。

繰り返す。

x = 2 ^ i * 5 ^ jを格納する代わりに、(i、j)を格納してカスタム比較関数を使用できます。


1
ヒープは、その操作にlg n時間を与え、複雑さをn lg nに押し上げます。
corsiKa 2011年

@glow:はい、ただし、これまでに投稿されたO(n)ソリューションは表示されません:-)

@abel:そのコメントは古いです:-)彼も(1,1)から(4,0)に行くのに問題があるようです。しかし、それをヤングの行列(vladの答えを参照)として見ると、実際にはO(n)時間アルゴリズムが許可されます。

@モロン:私はその解決策に何か問題があるとは思わない。確かに、最初にチェックした30個の要素には何も問題はありません(これは(1,1)->(4,0)のケースをカバーします)。
abeln 2011年

@abel:うん、実際に実行しようとはしなかった:-)多分その正しさの簡単な証明もあるだろう。FWIW、すでに私の+1があります。

13

FIFOベースのソリューションでは、必要なストレージ容量が少なくなります。Pythonコード。

F = [[1, 0, 0]]             # FIFO [value, i, j]
i2 = -1; n2 = n5 = None     # indices, nexts
for i in range(1000):       # print the first 1000
    last = F[-1][:]
    print "%3d. %21d = 2^%d * 5^%d" % tuple([i] + last)
    if n2 <= last: i2 += 1; n2 = F[i2][:]; n2[0] *= 2; n2[1] += 1
    if n5 <= last: i2 -= 1; n5 = F.pop(0); n5[0] *= 5; n5[2] += 1
    F.append(min(n2, n5))

出力:

  0.                     1 = 2^0 * 5^0
  1.                     2 = 2^1 * 5^0
  2.                     4 = 2^2 * 5^0
 ...
998. 100000000000000000000 = 2^20 * 5^20
999. 102400000000000000000 = 2^27 * 5^17

6

これはO(n)関数型言語で行うのが非常に簡単です。リストl2^i*5^j数字は、単にとして定義することができ1、その後2*l5*l合併しました。Haskellでの表示は次のとおりです。

merge :: [Integer] -> [Integer] -> [Integer]
merge (a:as) (b:bs)   
  | a < b   = a : (merge as (b:bs))
  | a == b  = a : (merge as bs)
  | b > a   = b : (merge (a:as) bs)

xs :: [Integer]
xs = 1 : merge (map(2*)xs) (map(5*)xs)

このmerge関数は、一定の時間で新しい値を提供します。そうするmapので、そうしlます。


「k」は定義されていないと思います
Ither

2
union代わりに、この「マージ」関数を呼び出して、重複を削除します。mergeは、の一部として、mergesortその両方の入力シーケンスからの重複を保持する必要があります。Data.List.Ordered関連するものについてはパッケージを参照してください。
ネスは

1
の+1 Data.List.Ordered.union。それはそれを1行になります:xs = 1 : union (map (2*) xs) (map (5*) xs)
PHOB

@GaBorgulyaはい、リストの5倍が[1, 2, 4, 5,...]含まれて5*4いるため、が含まれています。
Thomas Ahle

1
@Phobはい、これがData.List.Ordered.union関数です。と混同しないでくださいData.List.union
Thomas Ahle

5

あなたはそれらの個々の指数とそれらの合計がどうなるかを追跡する必要があります

だから今から始めてf(0,0) --> 1 、そのうちの1つをインクリメントする必要があります。

f(1,0) = 2
f(0,1) = 5

2が次であることがわかります。また、合計が5を超えるまで、iの指数を増分できます。

必要なラウンド数になるまで、このように行ったり来たりし続けます。


はい、そうです。ラウンドごとに1つのO(1)操作を実行します。ラウンドを早期に行うこともありますが、そのラウンドに到達したときにそこで行う必要はないため、うまくいきます。
corsiKa 2011年

19
(1,1)から(4,0)にどうやって行くの?アルゴリズムが何であるかを詳しく説明してください。

問題は、2つの増分の可能性があるf(*,2)だけではないことですf(a1,b+1)>f(a2,b)。たとえば、それを見つけたからといって完了しているわけではありません。インクリメンタルアプローチは、最終的に、すでに出力したリージョンに隣接する無制限のペアを生成します。
来年の嵐2011年

@ user515430は、昼休みにできる以上の実装を提供しましたが、それを実現しようとしていました。
corsiKa

4

動的プログラミングを使用すると、O(n)でこれを行うことができます。真実は、iとjの値が0を与えることはなく、1を取得するには両方の値が0でなければならないということです。

TwoCount[1] = 0
FiveCount[1] = 0

// function returns two values i, and j
FindIJ(x) {
    if (TwoCount[x / 2]) {
        i = TwoCount[x / 2] + 1
        j = FiveCount[x / 2]
    }
    else if (FiveCount[x / 5]) {
        i = TwoCount[x / 2]
        j = FiveCount[x / 5] + 1
    }
}

この関数を呼び出すときは常に、iとjが設定されているかどうか、それらがnullでないかどうかを確認してから、データを入力TwoCountしてFiveCount


C ++の答え。悪いコーディングスタイルで申し訳ありませんが、私は急いでいます:(

#include <cstdlib>
#include <iostream>
#include <vector>

int * TwoCount;
int * FiveCount;

using namespace std;

void FindIJ(int x, int &i, int &j) {
        if (x % 2 == 0 && TwoCount[x / 2] > -1) {
                cout << "There's a solution for " << (x/2) << endl;
                i = TwoCount[x / 2] + 1;
                j = FiveCount[x / 2];
        } else if (x % 5 == 0 && TwoCount[x / 5] > -1) {
                cout << "There's a solution for " << (x/5) << endl;
                i = TwoCount[x / 5];
                j = FiveCount[x / 5] + 1;
        }    
}

int main() {
        TwoCount = new int[200];
        FiveCount = new int[200];

        for (int i = 0; i < 200; ++i) {
                TwoCount[i] = -1;
                FiveCount[i] = -1;
        }

        TwoCount[1] = 0;
        FiveCount[1] = 0;

        for (int output = 2; output < 100; output++) {
                int i = -1;
                int j = -1;
                FindIJ(output, i, j);
                if (i > -1 && j > -1) {
                        cout << "2^" << i << " * " << "5^" 
                                     << j << " = " << output << endl;
                        TwoCount[output] = i;
                        FiveCount[output] = j;
                }
        }    
}

明らかに、配列以外のデータ構造を使用してストレージなどを動的に増やすことができます。これは、それが機能することを証明するための単なるスケッチです。


4
これは興味深い答えのように見えますが、実際にどのように機能するかはわかりません。詳細を追加していただけますか?
David Brunelle、2011年

自分で調べてみたら、どういう仕組みなのかよくわかりません。整数除算を仮定すると、3の場合も2の場合とまったく同じ結果が得られます。さらに、if条件がゼロ以外のテストである場合、ゼロ以外のエントリがないため、動作しません。
David Thornley、2011年

あなたが言うことのできない人のために、C ++バージョンを投稿しました。@Davidあなたのコメントは正しいですが、私の元のコードは疑似コードであり、スクリプト用語で考えていたため、整数ではない除算を行い、nullエントリと値0のエントリを区別していました
Mikhail

このコードはすべての自然数を列挙するため、以下の「アラバマでのロスト」による回答に対する@ThomasAhleのコメントごとに、シーケンスの数O(exp(sqrt(n)))を生成するためにを必要としnます。たとえば、ThomasAhleによって与えられた線形アルゴリズムが存在します。
Will Ness、2007

1
あなたが正しい。私の理解でO(n)n、印刷されたアイテムの数ではなく、最後の値であることを意味していました。関数型言語がどのように機能するか、またはマージが一定の時間でどのように機能するかはわかりませんが、彼の答えが私の賛成票を獲得しました
Mikhail

2

これを別の方向から見てみませんか。カウンターを使用して、元の式に対して可能な答えをテストします。疑似コードでごめんなさい。

for x = 1 to n
{
  i=j=0
  y=x
  while ( y > 1 )
  {
    z=y
    if y divisible by 2 then increment i and divide y by 2
    if y divisible by 5 then increment j and divide y by 5

    if y=1 then print i,j & x  // done calculating for this x

    if z=y then exit while loop  // didn't divide anything this loop and this x is no good 
  }
}

シーケンスO(4^sqrt(n))nth数はほぼそのサイズであるため、これはほぼ実行されます。
Thomas Ahle、2011

2

これはOEISの関連エントリです。

最初の数項を生成することにより、順序付けられたシーケンスを取得することは可能であると思われます。

1 2 4 5

次に、2番目の項から始めて、4と5を掛けて次の2つを取得します。

1 2 4 5 8 10

1 2 4 5 8 10 16 20

1 2 4 5 8 10 16 20 25

等々...

直感的にはこれは正しいように見えますが、もちろん証拠はありません。


2
間違った:( [1 2 4 5 8 10 16 20 25 32 40 50 64 80 100 125 128 160 200 250 256 320 400 500 625 ]しかし500 <= 2 ^ 9 512 <625
GaBorgulya

1
@ NateKerkhofs、512が生成されますが、512は既に生成された625よりも小さいため、順不同です。アルゴリズムは出力を整理するためにさらにロジックが必要になります-したがって、アルゴリズムは提案されているほど単純ではなく、同じアルゴリズムでもありません。
GordonBGood 2016

1

log_2(5)= 2.32。このことから、2 ^ 2 <5および2 ^ 3> 5であることがわかります。

可能な答えのマトリックスを見てください:

j/i  0   1   2   3   4   5
 0   1   2   4   8  16  32
 1   5  10  20  40  80 160 
 2  25  50 100 200 400 800
 3 125 250 500 ...

この例では、順番に番号を選択します。順序付けは次のようになります。

j/i  0   1   2   3   4   5
 0   1   2   3   5   7  10
 1   4   6   8  11  14  18
 2   9  12  15  19  23  27
 3  16  20  24...

すべての行は、それを開始する行の2列後ろから始まることに注意してください。たとえば、i = 0 j = 1はi = 2 j = 0の直後に来ます。

したがって、このパターンから導出できるアルゴリズムは(j> iと仮定)です。

int i = 2;
int j = 5;
int k;
int m;

int space = (int)(log((float)j)/log((float)i));
for(k = 0; k < space*10; k++)
{
    for(m = 0; m < 10; m++)
    {
        int newi = k-space*m;
        if(newi < 0)
            break;
        else if(newi > 10)
            continue;
        int result = pow((float)i,newi) * pow((float)j,m);
        printf("%d^%d * %d^%d = %d\n", i, newi, j, m, result);
    }
}   

注:ここのコードでは、iおよびjの指数の値を10未満に制限しています。このアルゴリズムを簡単に拡張して、他の任意の境界に適合させることができます。

注:このアルゴリズムの実行時間は、最初のn個の回答のO(n)です。

注:このアルゴリズムのスペースの複雑さはO(1)です。


あなたは「すべての行はそれを開始する行の後ろに2列開始します」と書いた。しかし、2 ^ 9 = 512と5 ^ 4 = 625、これは、行4には当てはまらない
GaBorgulya

@ user678105その通りです。このコードは機能しません。すみません。ログの丸めと、それが問題ではないと私の想定が原因で、このコードは機能しません。
KLee1

1
これを修正する方法は次のとおりです。積分係数を持つ点でいっぱいの(x、y)平面上に、(0,1)から(log2(5)、0)まで線を引きます。(0,0)は左上隅にあります。X軸は右に、Y軸は下に移動します。次に、1番目の線に垂直な(0,0)原点から線を引きます。次に、最初の線を2番目の線に沿って、原点から遠ざかるようにスライドさせ、整数座標点が交差するように収集します。{2,3,5}で生成されたシーケンスの場合、それは(i、j、k)空間で移動する平面になります。このアイデアをコードに変換できる場合は、一言お願いします。:)
ネスは

1

私の実装は次のアイデアに基づいています:

  • 2つのキューQ2とQ5を使用します。どちらも1で初期化されます。両方のキューをソートされた順序で保持します。
  • すべてのステップで、Q2またはQ5から最小の数値要素MINをデキューし、出力します。Q2とQ5の両方に同じ要素がある場合-両方を削除します。この番号を印刷します。これは基本的に2つの並べ替えられた配列のマージです-各ステップで最小の要素を選択して進みます。
  • MIN * 2をQ2に、MIN * 5をQ5にエンキューします。MINが以前のMIN番号よりも大きいため、この変更によって、ソートされるQ2 / Q5の不変条件が壊れることはありません。

例:

Start with 1 and 1 (to handle i=0;j=0 case):
  Q2: 1
  Q5: 1
Dequeue 1, print it and enqueue 1*2 and 1*5:
  Q2: 2
  Q5: 5
Pick 2 and add 2*2 and 2*5:
  Q2: 4
  Q5: 5 10
Pick 4 and add 4*2 and 4*5:
  Q2: 8
  Q5: 5 10 20
....

Javaのコード:

public void printNumbers(int n) {
    Queue<Integer> q2 = new LinkedList<Integer>();
    Queue<Integer> q5 = new LinkedList<Integer>();
    q2.add(1);
    q5.add(1);
    for (int i = 0; i < n; i++) {
        int a = q2.peek();
        int b = q5.peek();
        int min = Math.min(a, b);
        System.out.println(min);
        if (min == a) {
            q2.remove();
        }
        if (min == b) {
            q5.remove();
        }
        q2.add(min * 2);
        q5.add(min * 5);
    }
}

0

結果を計算し、ソートされたリストに入れて、一緒の値とiし、j


それはおそらくあなたのシーケンスの後半に穴をあけるでしょう。例えば、あなたは持っているでしょうが2^n*5^n2^(n+1)*5^(n-1)どちらか小さいです。
Thomas Ahle

@トーマス私はここであなたの論理に従うかどうかはわかりません。一方を計算する場合、なぜもう一方も計算しないのですか?
vlad

2
@vladあなたの制限を持っている必要がありiさんとjの、あなたがいないのですか?それ以外の場合は、並べ替えの状態になりません。したがって、単一の値を返すことはありません。しかしn、あなたが選ぶどんな制限についても、あなたのリストは不備があります。
Thomas Ahle

@トーマスあなたの議論はまだ意味をなさない。OPは彼の結果リストの終わりを指定していません。もしそうなら、あなたは最大iとを見つけることができますj
vlad

1
@vlad私があなたの答えを読んだとき、あなたは最初に「結果」/ 2^i*5^j値を計算し、次にそれらをソートします。「結果」の数に制限がない場合、どのようにしてソートのステップに進むことができますか?
Thomas Ahle、2011

0

Edsger Dijkstra(http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF)によってuser515430によって実装されたアルゴリズムは、可能な限り高速です。2^i * 5^j「特別な番号」の形式であるすべての番号を呼び出します。今vladsの答えはO(i*j)、二重のアルゴリズムであるでしょうが、1つは特別な数値を生成するためのものでO(i*j)、もう1つはそれらをソートするためのものです(リンクされた記事に従っても)O(i*j)

ただし、ダイクストラのアルゴリズムを確認してみましょう(以下を参照)。この場合nは、生成する特別な数値の量なので、に等しくなりi*jます。1回ループし、1 -> nすべてのループで一定のアクションを実行します。したがって、このアルゴリズムも同様O(i*j)です。また、かなり高速な定数も使用します。

GMP(C ++ラッパー)を使用したC ++での実装、およびへの依存関係はboost::lexical_cast簡単に削除できます(私は怠惰で、Boostを使用していませんか?)。でコンパイルg++ -O3 test.cpp -lgmpxx -o test。オンQ6600のUbuntu 10.10をtime ./test 1000000与えます1145ms

#include <iostream>
#include <boost/lexical_cast.hpp>
#include <gmpxx.h>

int main(int argc, char *argv[]) {
    mpz_class m, x2, x5, *array, r;
    long n, i, i2, i5;

    if (argc < 2) return 1;

    n = boost::lexical_cast<long>(argv[1]);

    array = new mpz_class[n];
    array[0] = 1;

    x2 = 2;
    x5 = 5;
    i2 = i5 = 0;

    for (i = 1; i != n; ++i) {
        m = std::min(x2, x5);

        array[i] = m;

        if (x2 == m) {
            ++i2;
            x2 = 2 * array[i2];
        }

        if (x5 == m) {
            ++i5;
            x5 = 5 * array[i5];
        }
    }

    delete [] array;
    std::cout << m << std::endl;

    return 0;
}

0

iを行、jを列として行列を描くと、パターンがわかります。i = 0から開始し、行列の最上部に到達するまで(j> = 0)、2行上に1列右に移動するだけで行列をトラバースします。次に、i + 1などを実行します。

したがって、i = 7の場合、次のように移動します。

7, 0 -> 5, 1 -> 3, 2 -> 1, 3

そしてi = 8の場合:

8, 0 -> 6, 1 -> 4, 2 -> 2, 3 -> 0, 4

ここでは、Javaではi = 9になっています。行列の位置(i、j)と値を出力します。

for(int k = 0; k < 10; k++) {

    int j = 0;

    for(int i = k; i >= 0; i -= 2) {

        int value = (int)(Math.pow(2, i) * Math.pow(5, j));
        System.out.println(i + ", " + j + " -> " + value);
        j++;
    }
}

0

私の直感

i = 0、j = 0の初期値を1とすると、(2 ^ 1)(5 ^ 0)、(2 ^ 2)(5 ^ 0)、(2 ^ 0)として次の数値を作成できます*(5 ^ 1)、...つまり2,4,5 ..

いつでも私の数はxだとしましょう。次に、次の方法で次の番号を作成できます。

  • x * 2
  • x * 4
  • x * 5

説明

Since new numbers can only be the product with 2 or 5.
But 4 (pow(2,2)) is smaller than 5, and also we have to generate 
Numbers in sorted order.Therefore we will consider next numbers
be multiplied with 2,4,5.
Why we have taken x*4 ? Reason is to pace up i, such that it should not 
be greater than pace of j(which is 5 to power). It means I will 
multiply my number by 2, then by 4(since 4 < 5), and then by 5 
to get the next three numbers in sorted order.

テスト走行

We need to take an Array-list of Integers, let say Arr.

Also put our elements in Array List<Integers> Arr.
Initially it contains Arr : [1]
  • x = 1から始めましょう。

    次の3つの数値は1 * 2、1 * 4、1 * 5 [2、4、5]です。到着[1,2,4,5]

  • 今x = 2

    次の3つの数値は[4,8,10]です。{4はすでに発生しているため、無視します} [8,10]; 到着[1、2、4、5、8、10]

  • 今x = 4

    次の3つの数値[8,16,20] {8はすでに発生しています。無視してください} [16,20]到着[1,2,4,5,8,10,16,20]

  • x = 5

    次の3つの数値[10,20,25] {10,20}はすでに[25]が追加されているArr [1,2,4,5,8,10,16,20,25]

終了条件

 Terminating condition when Arr last number becomes greater 
 than (5^m1 * 2^m2), where m1,m2 are given by user.

分析

 Time Complexity : O(K) : where k is numbers possible between i,j=0 to 
 i=m1,j=m2.
 Space Complexity : O(K)

0

ちょうど来週期待することに興味があり、この質問を見つけました。

私の考えでは、5 ^ jほど大きなステップではなく、2 ^ iが増加します。したがって、次のjステップが大きくならない限り、iを増やします。

C ++での例(Qtはオプションです):

QFile f("out.txt"); //use output method of your choice here
f.open(QIODevice::WriteOnly);
QTextStream ts(&f);

int i=0;
int res=0;
for( int j=0; j<10; ++j )
{
    int powI = std::pow(2.0,i );
    int powJ = std::pow(5.0,j );
    while ( powI <= powJ  ) 
    {
        res = powI * powJ;
        if ( res<0 ) 
            break; //integer range overflow

        ts<<i<<"\t"<<j<<"\t"<<res<<"\n";
        ++i;
        powI = std::pow(2.0,i );

    }
}

出力:

i   j   2^i * 5^j
0   0   1
1   1   10
2   1   20
3   2   200
4   2   400
5   3   4000
6   3   8000
7   4   80000
8   4   160000
9   4   320000
10  5   3200000
11  5   6400000
12  6   64000000
13  6   128000000
14  7   1280000000

このソリューションはいくつかの組み合わせを見逃しています。例えば、それは私がそのことについては1とj> 1 = 1、J = 2どのような場合に=ケースを調べdoesntの...
フェデリコ・

@Federico:あなたは正しいです!6年の間隔で2回google-interviewsに失敗したのも不思議ではありませんが、ほぼ同じ質問です:-)
Valentin Heinitz

0

これが私の解決策です

#include <stdio.h>
#include <math.h>
#define N_VALUE 5
#define M_VALUE  5

int n_val_at_m_level[M_VALUE];

int print_lower_level_val(long double val_of_higher_level, int m_level)
{
int  n;
long double my_val;


for( n = n_val_at_m_level[m_level]; n <= N_VALUE; n++) {
    my_val =  powl(2,n) * powl(5,m_level);
    if(m_level != M_VALUE && my_val > val_of_higher_level) {
        n_val_at_m_level[m_level] = n;
        return 0;
    }
    if( m_level != 0) {
        print_lower_level_val(my_val, m_level - 1);
    }
    if(my_val < val_of_higher_level || m_level == M_VALUE) {
        printf("    %Lf n=%d m = %d\n", my_val, n, m_level);
    } else {
        n_val_at_m_level[m_level] = n;
        return 0;
    }
 }
 n_val_at_m_level[m_level] = n;
 return 0;
 }


 main()
 {
    print_lower_level_val(0, M_VALUE); /* to sort 2^n * 5^m */
 }

結果:

1.000000 n = 0 m = 0
2.000000 n = 1 m = 0
4.000000 n = 2 m = 0
5.000000 n = 0 m = 1
8.000000 n = 3 m = 0
10.000000 n = 1 m = 1
16.000000 n = 4 m = 0
20.000000 n = 2 m = 1
25.000000 n = 0 m = 2
32.000000 n = 5 m = 0
40.000000 n = 3 m = 1
50.000000 n = 1 m = 2
80.000000 n = 4 m = 1
100.000000 n = 2 m = 2
125.000000 n = 0 m = 3
160.000000 n = 5 m = 1
200.000000 n = 3 m = 2
250.000000 n = 1 m = 3
400.000000 n = 4 m = 2
500.000000 n = 2 m = 3
625.000000 n = 0 m = 4
800.000000 n = 5 m = 2
1000.000000 n = 3 m = 3
1250.000000 n = 1 m = 4
2000.000000 n = 4 m = 3
2500.000000 n = 2 m = 4
3125.000000 n = 0 m = 5
4000.000000 n = 5 m = 3
5000.000000 n = 3 m = 4
6250.000000 n = 1 m = 5
10000.000000 n = 4 m = 4
12500.000000 n = 2 m = 5
20000.000000 n = 5 m = 4
25000.000000 n = 3 m = 5
50000.000000 n = 4 m = 5
100000.000000 n = 5 m = 5

0

私はおそらく間違っていることを知っていますが、2、3、5のような多くの数を含まないため、ここには非常に単純なヒューリスティックがあります。任意のi、j 2 ^ i * 5 ^ jについて、次のシーケンスは2 ^(i-2)* 5 ^(j + 1)になることがわかっています。グーグルqであるため、簡単な解決策が必要です。

def func(i, j):
 print i, j, (2**i)*(5**j)

imax=i=2
j=0
print "i", "j", "(2**i)*(5**j)"

for k in range(20):
    func(i,j)
    j=j+1; i=i-2
    if(i<0):
        i = imax = imax+1
        j=0

これは次のような出力を生成します:

i j (2**i)*(5**j)
2 0 4
0 1 5
3 0 8
1 1 10
4 0 16
2 1 20
0 2 25
5 0 32
3 1 40
1 2 50
6 0 64
4 1 80
2 2 100
0 3 125
7 0 128
5 1 160
3 2 200
1 3 250
8 0 256
6 1 320

最大20、または200まで機能しますが、ある時点で、一部の数値をスキップしたり、間違った順序で出力したりします。
ネス

0

式でiまたはjをインクリメントしたときに実際に何が起こっているのかを説明すると 2^i * 5^jているのかを確認すると、別の2または別の5を乗算していることになります。問題を次のように述べます。値が大きいほど、解決策が明らかになります。

直感的に列挙できるルールは次のとおりです。

  • i > 1式に2のペア()がある場合は、2を5に置き換えて、次に大きい数を取得する必要があります。したがって、i -= 2およびj += 1
  • それ以外の場合、5(j > 0)がある場合は、3を2に置き換える必要があります。だからj -= 1i += 3
  • それ以外の場合は、値を最小限に増やすために、さらに2を指定する必要があります。i += 1

Rubyのプログラムは次のとおりです。

i = j = 0                                                                       
20.times do                                                                     
  puts 2**i * 5**j

  if i > 1                                                                      
    j += 1                                                                      
    i -= 2                                                                      
  elsif j > 0                                                                   
    j -= 1                                                                      
    i += 3                                                                      
  else                                                                          
    i += 1                                                                      
  end                                                                                                                                                               
end

「i」が4より大きくなることはないため、これは機能しません。したがって、32(2 ^ 5)の倍数は表示されません。
threenplusone 2016年

0

Javaコレクションの使用が許可されている場合、O(n ^ 2)にこれらの番号を含めることができます

public static void main(String[] args) throws Exception {
    int powerLimit = 7;  
     int first = 2;
     int second = 5;
    SortedSet<Integer> set = new TreeSet<Integer>();

    for (int i = 0; i < powerLimit; i++) {
        for (int j = 0; j < powerLimit; j++) {
            Integer x = (int) (Math.pow(first, i) * Math.pow(second, j));
            set.add(x);
        }
    }

    set=set.headSet((int)Math.pow(first, powerLimit));

    for (int p : set)
        System.out.println(p);
}

ここでpowerLimitは慎重に初期化する必要があります!! 必要な数に応じて。


これは間違った結果を生成します:2 ^ 8 * 256は2 ^ 6 * 5 = 320の前にありません。列挙領域は長方形ではなく三角形です。
Will Ness

@WillNessどうやって?? powerLimit = 9を設定すると、このスニペットは次の数値を返します1 2 4 5 8 10 16 20 25 32 40 50 64
80100125128160200250256320400500

いいえ、100個の数値を生成します。どこでやめるべきかをどうやって知っていますか?あなたはこれを説明しなければなりません。---コードスニペットに存在する7を参照しました。これが有効な回答となるためには、特定の数の制限を設定する方法と、過剰に生成される数を正確に説明する必要があります。
ウィルネス

0

Scalaでの私の試みは次のとおりです。

case class IndexValue(twosIndex: Int, fivesIndex: Int)
case class OutputValues(twos: Int, fives: Int, value: Int) {
  def test(): Boolean = {
    Math.pow(2,  twos) * Math.pow(5, fives) == value
  }
}

def run(last: IndexValue = IndexValue(0, 0), list: List[OutputValues] = List(OutputValues(0, 0, 1))): List[OutputValues] = {
  if (list.size > 20) {
    return list
  }

  val twosValue = list(last.twosIndex).value * 2
  val fivesValue = list(last.fivesIndex).value * 5

  if (twosValue == fivesValue) {
    val lastIndex = IndexValue(last.twosIndex + 1, last.fivesIndex + 1)
    val outputValues = OutputValues(value = twosValue, twos = list(last.twosIndex).twos + 1, fives = list(last.fivesIndex).fives + 1)
    run(lastIndex, list :+ outputValues)
  } else if (twosValue < fivesValue) {
    val lastIndex = IndexValue(last.twosIndex + 1, last.fivesIndex)
    val outputValues = OutputValues(value = twosValue, twos = list(last.twosIndex).twos + 1, fives = list(last.twosIndex).fives)
    run(lastIndex, list :+ outputValues)
  } else {
    val lastIndex = IndexValue(last.twosIndex, last.fivesIndex + 1)
    val outputValues = OutputValues(value = fivesValue, twos = list(last.fivesIndex).twos, fives = list(last.fivesIndex).fives + 1)
    run(lastIndex, list :+ outputValues)
  }
}

val initialIndex = IndexValue(0, 0)
run(initialIndex, List(OutputValues(0, 0, 1))) foreach println

出力:

OutputValues(0,0,1)
OutputValues(1,0,2)
OutputValues(2,0,4)
OutputValues(0,1,5)
OutputValues(3,0,8)
OutputValues(1,1,10)
OutputValues(4,0,16)
OutputValues(2,1,20)
OutputValues(0,2,25)
OutputValues(5,0,32)
OutputValues(3,1,40)
OutputValues(1,2,50)
OutputValues(6,0,64)
OutputValues(4,1,80)
OutputValues(2,2,100)
OutputValues(0,3,125)
OutputValues(7,0,128)
OutputValues(5,1,160)
OutputValues(3,2,200)
OutputValues(1,3,250)
OutputValues(8,0,256)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.