Pertanyaan wawancara Google yang rumit

169

Seorang teman saya sedang mewawancarai untuk suatu pekerjaan. Salah satu pertanyaan wawancara membuat saya berpikir, hanya ingin umpan balik.

Ada 2 bilangan bulat non-negatif: i dan j. Dengan persamaan berikut, cari solusi (optimal) untuk beralih lebih dari i dan j sedemikian rupa sehingga output diurutkan.

2^i * 5^j

Jadi beberapa putaran pertama akan terlihat seperti ini:

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

Cobalah sekuat tenaga, saya tidak bisa melihat polanya. Pikiran Anda?

Chris Eberle
sumber
63
Algoritma optimal dalam hal waktu programmer adalah untuk menghasilkan dengan dua loop bersarang, lalu mengurutkan. Mengapa mereka mengajukan pertanyaan seperti ini?
Tom Zych
21
Anda mungkin dapat menentukan titik transisi dengan melihat nomor mana yang lebih besar. 2^2 < 5tetapi 2^3 > 5pada saat itu Anda meningkatkan j. Saya pikir Anda dapat menghasilkan output dalam O (n) daripada O (nlgn). @ tom-zynch dua loop bersarang adalah O (n ^ 2). Pertanyaan ini sangat valid
Mikhail
1
Hanya ada satu output, jadi solusi optimal adalah O (n). Baca solusi saya di bawah ini
Mikhail
3
Pertanyaan serupa telah ditanggapi sebelumnya rupanya: stackoverflow.com/questions/4600048/nth-ugly-number .
1
... dan OP mungkin harus sudah memilih jawaban. Lagipula, dia sudah punya banyak yang bagus.
abeln

Jawaban:

123

Dijkstra mendapatkan solusi fasih dalam "A Disiplin Pemrograman". Dia menghubungkan masalah itu dengan Hamming. Inilah implementasi solusi Dijkstra saya.

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;
}
pengguna515430
sumber
18
Tautan yang relevan: en.wikipedia.org/wiki/Regular_number#Algorithms . Saya kira ini bukan pertanyaan wawancara yang sangat bagus. Berikut ini adalah (makalah tulisan tangan) oleh Dijkstra di mana ia menyediakan dan membuktikan algoritme untuk masalah ini: cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF
Elian Ebbing
Ketika tujuannya adalah "untuk beralih lebih dari i dan j" Anda membutuhkan kapasitas penyimpanan yang lebih sedikit, sebuah FIFO sudah cukup. Lihat solusi Python saya.
GaBorgulya
7
Ketika tujuannya adalah "untuk beralih dari i dan j", itu bukan masalah yang sama.
mhum
Ini adalah implementasi yang sangat bagus, menggunakan memori minimum. Ini adalah memori linier walaupun Anda hanya menginginkan satu nomor saja.
Thomas Ahle
1
@ThomasAhle Tidak tahu apakah Anda melihat ini tetapi memiliki kode pada akhirnya yang mampu menghitung angka ke-n secara terpisah. Seperti misalnya angka sepersejuta .
Will Ness
47

di sini adalah cara yang lebih baik untuk melakukannya (lebih halus dari jawaban saya sebelumnya, yaitu):

bayangkan angka-angka ditempatkan dalam matriks:

     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

apa yang perlu Anda lakukan adalah 'berjalan' matriks ini, mulai dari (0,0). Anda juga perlu melacak apa kemungkinan langkah Anda selanjutnya. Ketika Anda mulai pada (0,0)Anda hanya memiliki dua opsi: baik (0,1)atau (1,0): karena nilai (0,1)lebih kecil, Anda memilih itu. kemudian lakukan hal yang sama untuk pilihan Anda berikutnya (0,2)atau (1,0). Sejauh ini, Anda memiliki daftar berikut: 1, 2, 4. Langkah Anda berikutnya adalah (1,0)karena nilainya lebih kecil dari (0,3). Namun, Anda sekarang memiliki tiga pilihan untuk langkah selanjutnya: baik (0,3), atau (1,1), atau (2,0).

Anda tidak perlu matriks untuk mendapatkan daftar, tetapi Anda perlu melacak semua pilihan Anda (yaitu ketika Anda mencapai 125+, Anda akan memiliki 4 pilihan).

vlad
sumber
Saya memilih ini karena saya berpikir di jalur yang sama, tetapi dalam kasus umum, bukankah ini akan menjadi seperti O (i ^ 2 * j)? Anda harus memeriksa beberapa angka untuk setiap angka yang Anda hasilkan.
Tom Zych
1
@ Tom Anda harus memeriksa lebih dari satu angka, tetapi tidak terlalu buruk: ketika Anda menampilkan angka antara 125 dan 625, Anda perlu melihat 4 nilai. antara 625 dan 3025, Anda melihat 5 nilai. jadi sungguh, itu jmemeriksa untuk setiap 1 output
vlad
+1: Gabungkan dengan pertanyaan ini: stackoverflow.com/questions/5000836/search-algorithm dan sepertinya kami memiliki solusi O (n).
@Moron sialan, saya tidak ingin membayar $ 25 untuk algoritma itu, tetapi itu terlihat menarik.
vlad
1
sebenarnya, j ~ n^0.5untuk n th-nilai secara berurutan, karena nnilai-nilai mengisi area pada i x jpesawat. Jadi algo ini adalah O(n^1.5)waktu, dengan O(n^0.5)ruang. Tapi ada algo waktu linier dengan ruang yang sama n^0.5, dan algo-heap mini dari jawaban di bawah ini adalah O(n*log(n))waktu dengan n^0.5ruang yang sama .
Will Ness
25

Gunakan Min-heap.

Masukkan 1.

ekstrak-Min. Katakanlah Anda mendapatkan x.

Dorong 2x dan 5x ke tumpukan.

Ulang.

Alih-alih menyimpan x = 2 ^ i * 5 ^ j, Anda dapat menyimpan (i, j) dan menggunakan fungsi perbandingan khusus.


sumber
1
Tumpukan akan memberi lg n waktu pada operasinya, yang mendorong kompleksitas ke n lg n.
corsiKa
@glow: Ya, saya tidak melihat solusi O (n) yang diposting sejauh ini :-)
@abel: Komentar itu sudah tua :-) Sepertinya dia akan mengalami masalah mulai dari (1,1) menjadi (4,0) juga. Tetapi melihatnya sebagai matriks anak muda (lihat jawaban vlad) sebenarnya memungkinkan algoritma waktu O (n).
@Moron: Saya kira tidak ada yang salah dengan solusi itu. Tentu saja tidak ada yang salah dalam 30 elemen pertama, yang baru saja saya periksa sekarang (yang akan mencakup kasus (1,1) -> (4,0)).
abeln
@abel: Ya tidak benar-benar mencoba menjalankannya :-) Mungkin ada bukti yang mudah tentang kebenarannya juga. FWIW, sudah memiliki +1 saya.
13

Solusi berbasis FIFO membutuhkan kapasitas penyimpanan yang lebih sedikit. Kode 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))

keluaran:

  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
GaBorgulya
sumber
6

Ini sangat mudah dilakukan O(n)dalam bahasa fungsional. Daftar ldari 2^i*5^jnomor dapat hanya didefinisikan sebagai 1kemudian 2*ldan 5*lbergabung. Berikut ini tampilannya di 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)

The mergeFungsi memberikan nilai baru dalam waktu yang konstan. Begitu juga mapdan karenanya juga begitu l.

Thomas Ahle
sumber
Saya pikir 'k' tidak didefinisikan
Ither
2
sebut saja fungsi "gabungan" ini union , karena menghapus duplikat. merge, sebagai bagian dari mergesort, harus mempertahankan duplikat yang berasal dari kedua urutan inputnya. Lihat Data.List.Orderedpaket untuk hal-hal terkait.
Will Ness
1
+1 untuk Data.List.Ordered.union. Itu membuatnya menjadi satu baris:xs = 1 : union (map (2*) xs) (map (5*) xs)
Phob
@ GaBorgulya Ya, itu termasuk lima kali daftar [1, 2, 4, 5,...] jadi termasuk 5*4.
Thomas Ahle
1
@ Phob Ya, ini Data.List.Ordered.unionfungsinya. Jangan bingung denganData.List.union .
Thomas Ahle
5

Anda harus melacak eksponen individu mereka, dan berapa jumlah mereka

jadi Anda mulai dengan f(0,0) --> 1 sekarang Anda harus menambah salah satunya:

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

jadi kita tahu 2 adalah yang berikutnya - kita juga tahu kita bisa menaikkan eksponen sampai jumlah surpases 5.

Anda terus bolak-balik seperti ini sampai Anda berada di jumlah putaran deisred Anda.

corsiKa
sumber
Ya itu. Anda melakukan satu operasi O (1) untuk setiap putaran. Kadang-kadang Anda melakukan putaran lebih awal, tetapi ketika Anda sampai ke putaran itu Anda tidak harus melakukannya di sana, sehingga berhasil dengan sendirinya.
corsiKa
19
Bagaimana Anda beralih dari (1,1) ke (4,0)? Tolong jelaskan apa itu algoritma Anda.
Masalahnya adalah, Anda tidak hanya memiliki dua kemungkinan tambahan - misalnya, Anda tidak selesai dengan f(*,2)hanya karena Anda menemukan itu f(a1,b+1)>f(a2,b). Pendekatan tambahan pada akhirnya akan menghasilkan jumlah pasangan yang tidak terbatas yang berdekatan dengan wilayah yang sudah Anda hasilkan.
badai
@ user515430 memberikan implementasi yang lebih dari yang bisa saya lakukan saat istirahat makan siang, tapi itulah yang saya coba lakukan.
corsiKa
4

Menggunakan pemrograman dinamis, Anda dapat melakukan ini di O (n). Kebenaran dasarnya adalah bahwa tidak ada nilai i dan j yang dapat memberi kita 0, dan untuk mendapatkan 1 nilai keduanya harus 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
    }
}

Setiap kali Anda memanggil fungsi ini periksa apakah i dan j diatur, jika tidak nol, maka isi TwoCountdanFiveCount


Jawaban C ++. Maaf untuk gaya pengkodean yang buruk, tapi saya sedang terburu-buru :(

#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;
                }
        }    
}

Jelas Anda dapat menggunakan struktur data selain array untuk meningkatkan penyimpanan Anda secara dinamis, dll. Ini hanya sketsa untuk membuktikan bahwa itu berfungsi.

Mikhail
sumber
4
Ini sepertinya jawaban yang menarik, tetapi saya gagal melihat cara kerjanya. Bisakah Anda menambahkan lebih detail?
David Brunelle
Setelah mempelajarinya sendiri, saya benar-benar tidak melihat cara kerjanya. Dengan asumsi pembagian integer, itu akan memberikan hasil yang sama persis untuk 3 seperti untuk 2. Selain itu, jika kondisi jika tes untuk tidak nol, itu tidak akan pernah berhasil, karena tidak ada entri yang tidak nol.
David Thornley
Diposting versi C ++ untuk semua yang Anda katakan. @David Komentar Anda benar, tetapi kode asli saya adalah kode semu dan saya berpikir dalam istilah scripting, jadi divisi tidak-integer dan membedakan antara entri nol dan masuknya nilai 0
Mikhail
kode ini menyebutkan semua bilangan asli, jadi, per komentar dari @ThomasAhle untuk jawaban oleh "Lost in Alabama" di bawah, diperlukan O(exp(sqrt(n))), untuk menghasilkan nnomor urut. Algoritma linear ada, misalnya seperti yang diberikan oleh ThomasAhle.
Will Ness
1
Kamu benar. Dalam pemahaman saya O(n)berarti nmenjadi nilai terakhir, bukan jumlah barang yang dicetak, yang tidak benar. Saya tidak tahu bagaimana bahasa fungsional bekerja, atau bagaimana penggabungan bekerja dalam waktu yang konstan, tetapi jawabannya membuat saya merasa lebih baik
Mikhail
2

Mengapa tidak mencoba melihat ini dari arah lain. Gunakan penghitung untuk menguji kemungkinan jawaban terhadap formula asli. Maaf untuk kode semu.

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 
  }
}
Hilang di Alabama
sumber
Ini berjalan sekitar O(4^sqrt(n))karena nthjumlah urutan kira-kira sebesar itu.
Thomas Ahle
2

Ini adalah entri yang relevan di OEIS.

Tampaknya mungkin untuk mendapatkan urutan yang diurutkan dengan menghasilkan beberapa istilah pertama, katakanlah

1 2 4 5

dan kemudian, mulai dari istilah kedua, mengalikan dengan 4 dan 5 untuk mendapatkan dua berikutnya

1 2 4 5 8 10

1 2 4 5 8 10 16 20

1 2 4 5 8 10 16 20 25

dan seterusnya...

Secara intuitif, ini tampaknya benar, tetapi tentu saja bukti tidak ada.

abeln
sumber
2
Salah :( [1 2 4 5 8 10 16 20 25 32 40 50 64 80 100 125 128 160 256 320 400 500 625 ] Namun 500 <512 = 2 ^ 9 <625.
GaBorgulya
1
@NateKerkhofs, 512 dihasilkan tetapi tidak berfungsi karena 512 kurang dari 625 yang sudah dihasilkan; Algoritma akan membutuhkan logika lebih lanjut untuk mengatur output - Dengan demikian algoritma tidak sesederhana yang diusulkan dan bukan algoritma yang sama sekali.
GordonBGood
1

Anda tahu bahwa log_2 (5) = 2.32. Dari ini kita perhatikan bahwa 2 ^ 2 <5 dan 2 ^ 3> 5.

Sekarang lihatlah matriks jawaban yang memungkinkan:

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 ...

Sekarang, untuk contoh ini, pilih angka dalam urutan. Akan ada pemesanan:

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...

Perhatikan bahwa setiap baris dimulai 2 kolom di belakang baris yang memulainya. Misalnya, i = 0 j = 1 datang langsung setelah i = 2 j = 0.

Algoritma yang dapat kita peroleh dari pola ini adalah (asumsikan 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);
    }
}   

CATATAN: Kode di sini membatasi nilai eksponen i dan j menjadi kurang dari 10. Anda dapat dengan mudah memperluas algoritma ini agar sesuai dengan batas arbitrer lainnya.

CATATAN: Waktu berjalan untuk algoritma ini adalah O (n) untuk n jawaban pertama.

CATATAN: Kompleksitas ruang untuk algoritma ini adalah O (1)

KLee1
sumber
Anda menulis "setiap baris dimulai 2 kolom di belakang baris yang memulainya". Namun 2 ^ 9 = 512 dan 5 ^ 4 = 625, jadi ini tidak benar untuk baris 4.
GaBorgulya
@ user678105 Anda benar. Kode ini tidak berfungsi. Maaf semuanya Kode ini tidak berfungsi karena pembulatan log dan asumsi saya bahwa itu tidak masalah.
KLee1
1
Begini cara Anda memperbaikinya. Pada bidang (x, y) yang penuh dengan titik dengan koefisien integral, buat garis dari (0,1) hingga (log2 (5), 0). (0,0) ada di sudut kiri atas. Sumbu X pergi ke kanan, sumbu Y turun. Sekarang gambarlah garis dari titik asal (0,0) yang tegak lurus dengan garis pertama. Sekarang geser baris pertama sepanjang yang kedua, lebih jauh dan lebih jauh dari asal, dan kumpulkan titik koordinat bilangan ketika mereka dilintasi. Untuk urutan {2,3,5} yang dihasilkan, itu akan menjadi sebuah pesawat yang bergerak melintasi, dalam ruang (i, j, k). Jika Anda dapat menerjemahkan ide ini menjadi kode, beri saya teriakan. :)
Will Ness
1

Implementasi saya didasarkan pada ide-ide berikut:

  • Gunakan dua antrian Q2 dan Q5, keduanya diinisialisasi dengan 1. Kami akan menjaga kedua antrian dalam urutan.
  • Pada setiap langkah, keluarkan elemen nomor MIN terkecil dari Q2 atau Q5 dan cetaklah. Jika keduanya Q2 dan Q5 memiliki elemen yang sama - hapus keduanya. Cetak nomor ini. Ini pada dasarnya adalah penggabungan dua array yang diurutkan - pada setiap langkah pilih elemen terkecil dan maju.
  • Enqueue MIN * 2 hingga Q2 dan MIN * 5 hingga Q5. Perubahan ini tidak mematahkan invarian Q2 / Q5 yang sedang diurutkan, karena MIN lebih tinggi dari nomor MIN sebelumnya.

Contoh:

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
....

Kode di Jawa:

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);
    }
}
ejboy
sumber
0

menghitung hasilnya dan memasukkannya ke dalam daftar yang disortir, bersama dengan nilai untuk idanj

vlad
sumber
Itu mungkin akan memberi Anda lubang di akhir urutan Anda. Misalnya Anda akan 2^n*5^ntetapi tidak 2^(n+1)*5^(n-1)yang lebih kecil.
Thomas Ahle
@ Thomas Saya tidak yakin saya mengikuti logika Anda di sini. Jika Anda menghitung satu, mengapa Anda juga tidak menghitung yang lain?
vlad
2
@lad Anda harus memiliki batas pada Anda idan j, bukan? Kalau tidak, Anda tidak akan pernah sampai ke status penyortiran, dan karenanya Anda tidak akan pernah mengembalikan nilai tunggal. Tetapi untuk batas apa pun yang nAnda pilih, daftar Anda akan cacat.
Thomas Ahle
@ Thomas argumen Anda masih tidak masuk akal. OP tidak pernah menentukan akhir dari daftar hasilnya. Jika ya, Anda dapat menemukan maks idan j.
vlad
1
@vlad Saat saya membaca jawaban Anda, Anda pertama-tama menghitung "hasil" / 2^i*5^jnilai - nilai, dan kemudian mengurutkannya. Jika Anda tidak memiliki "hasil" dalam jumlah terbatas, bagaimana Anda akan sampai pada langkah penyortiran?
Thomas Ahle
0

Algoritme yang diimplementasikan oleh user515430 oleh Edsger Dijkstra (http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF) mungkin secepat yang Anda bisa dapatkan. Saya memanggil setiap nomor yang merupakan bentuk 2^i * 5^j"nomor khusus". Sekarang jawaban vlads akan O(i*j)tetapi dengan algoritma ganda, satu untuk menghasilkan angka khusus O(i*j)dan satu untuk menyortirnya (sesuai dengan artikel yang ditautkan jugaO(i*j) .

Tapi mari kita periksa algoritma Dijkstra (lihat di bawah). Dalam hal ini nadalah jumlah angka khusus yang kami hasilkan, sehingga sama dengan i*j. Kami mengulang sekali, 1 -> ndan di setiap putaran kami melakukan tindakan konstan. Jadi algoritma ini jugaO(i*j) . Dan dengan konstanta cepat yang sangat menyala juga.

Implementasi saya di C ++ dengan GMP (pembungkus C ++), dan ketergantungan boost::lexical_cast, meskipun itu dapat dengan mudah dihapus (saya malas, dan siapa yang tidak menggunakan Boost?). Disusun dengan g++ -O3 test.cpp -lgmpxx -o test. Pada Q6600 Ubuntu 10.10 time ./test 1000000memberi 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;
}
orlp
sumber
0

Jika Anda menggambar matriks dengan i sebagai baris dan j sebagai kolom, Anda dapat melihat polanya. Mulailah dengan i = 0 dan kemudian hanya melintasi matriks dengan naik 2 baris dan kanan 1 kolom sampai Anda mencapai bagian atas matriks (j> = 0). Lalu lanjutkan +1, dll ...

Jadi untuk i = 7 Anda bepergian seperti ini:

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

Dan untuk i = 8:

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

Ini dia di Java naik ke i = 9. Mencetak posisi matriks (i, j) dan nilainya.

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++;
    }
}
tembem
sumber
0

Intuisi saya :

Jika saya mengambil nilai awal sebagai 1 di mana i = 0, j = 0, maka saya dapat membuat angka berikutnya sebagai (2 ^ 1) (5 ^ 0), (2 ^ 2) (5 ^ 0), (2 ^ 0) * (5 ^ 1), ... yaitu 2,4,5 ..

Katakanlah kapan saja nomor saya adalah x. maka saya dapat membuat angka berikutnya dengan cara berikut:

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

Penjelasan :

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.

Uji Coba

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]
  • Mari kita mulai dengan x = 1.

    Tiga angka berikutnya adalah 1 * 2, 1 * 4, 1 * 5 [2,4,5]; Rangkaian [1,2,4,5]

  • Sekarang x = 2

    Tiga angka berikutnya adalah [4,8,10] {Karena 4 sudah terjadi, kita akan mengabaikannya} [8,10]; Rancangan [1,2,4,5,8,10]

  • Sekarang x = 4

    Tiga angka berikutnya [8,16,20] {8 sudah terjadi abaikan saja} [16,20] Atur [1,2,4,5,8,10,16,20]

  • x = 5

    Tiga angka berikutnya [10,20,25] {10,20} sudah jadi [25] ditambahkan Arr [1,2,4,5,8,10,16,20,25]

Kondisi Pengakhiran

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

Analisis

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

Hanya ingin tahu apa yang diharapkan minggu depan dan telah menemukan pertanyaan ini.

Saya pikir, idenya adalah 2 ^ i meningkat tidak dalam langkah besar sebesar 5 ^ j. Jadi tambahlah saya selama j-step berikutnya tidak akan lebih besar.

Contoh dalam C ++ (Qt adalah opsional):

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 );

    }
}

Hasil:

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
Valentin Heinitz
sumber
Solusi ini melewatkan beberapa kombinasi. Misalnya, ia tidak memeriksa kasus di mana i = 1, j = 2 setiap kasus di mana saya = 1 dan j> 1 dalam hal ini ..
Federico
@Federico: Anda benar! Tidak heran mengapa saya telah gagal google-interview dua kali dengan interval 6 tahun tetapi pertanyaan yang hampir sama :-)
Valentin Heinitz
0

Ini solusinya

#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 */
 }

Hasil:

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
dhanasubbu
sumber
0

Saya tahu saya mungkin salah tetapi ada heuristik yang sangat sederhana di sini karena tidak melibatkan banyak angka seperti 2,3,5. Kita tahu bahwa untuk setiap i, j2 ^ i * 5 ^ j urutan selanjutnya adalah 2 ^ (i-2) * 5 ^ (j + 1). Menjadi google q itu harus memiliki solusi sederhana.

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

Ini menghasilkan output sebagai:

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
d1val
sumber
mungkin bekerja hingga 20, atau 200, tetapi pada titik tertentu akan mulai melewatkan beberapa angka dan / atau mengeluarkannya dengan urutan yang salah.
Will Ness
0

Jika Anda mengikuti apa yang sebenarnya terjadi ketika kita menambah i atau j dalam ekspresi 2^i * 5^j , Anda dapat mengalikan dengan 2 atau yang lain 5. Jika kami menyatakan kembali masalah sebagai - diberi nilai tertentu dari i dan j, bagaimana Anda menemukan berikutnya nilai yang lebih besar, solusinya menjadi jelas.

Berikut adalah aturan yang dapat kami sebutkan secara intuitif:

  • Jika ada pasangan 2s ( i > 1) dalam ekspresi, kita harus menggantinya dengan 5 untuk mendapatkan angka terbesar berikutnya. Jadi, i -= 2dan j += 1.
  • Kalau tidak, jika ada 5 ( j > 0), kita perlu menggantinya dengan tiga 2s. Jadi j -= 1dan i += 3.
  • Kalau tidak, kita hanya perlu memasok 2 lagi untuk meningkatkan nilai minimum. i += 1.

Berikut programnya di 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
slowpoison
sumber
Ini tidak berfungsi karena 'saya' tidak pernah menjadi lebih besar dari 4, jadi tidak ada kelipatan 32 (2 ^ 5) yang akan pernah muncul.
threenplusone
0

Jika kita diizinkan menggunakan Koleksi java maka kita dapat memiliki nomor ini dalam 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);
}

Sini powerLimit harus diinisialisasi dengan sangat hati-hati !! Tergantung pada berapa banyak angka yang Anda inginkan.

kavi temre
sumber
ini menghasilkan hasil yang salah: 2 ^ 8 = 256 hilang sebelum 2 ^ 6 * 5 = 320. area enumerasi berbentuk segitiga, bukan persegi panjang.
Will Ness
@ WillNess Bagaimana ?? Ketika saya mengatur powerLimit = 9, cuplikan ini mengembalikan angka-angka berikut 1 2 4 5 8 10 16 20 25 32 40 50 64 80 100 125 128 160 200 250 256 320 400 500
kavi temre
tidak, ini menghasilkan 100 angka. bagaimana kamu tahu harus berhenti di mana? Anda harus menjelaskan ini. --- Saya merujuk ke 7 yang ada dalam cuplikan kode Anda. agar ini menjadi jawaban yang valid, Anda harus menjelaskan dengan tepat cara menetapkan batas jumlah angka tertentu, dan berapa banyak angka yang akan diproduksi berlebih .
Will Ness
0

Inilah upaya saya dengan 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

Keluaran:

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)
nishnet2002
sumber