Câu hỏi phỏng vấn của Google


169

Một người bạn của tôi đang phỏng vấn cho một công việc. Một trong những câu hỏi phỏng vấn khiến tôi suy nghĩ, chỉ muốn một số phản hồi.

Có 2 số nguyên không âm: i và j. Cho phương trình sau, tìm một giải pháp (tối ưu) để lặp qua i và j theo cách mà đầu ra được sắp xếp.

2^i * 5^j

Vì vậy, một vài vòng đầu tiên sẽ như thế này:

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

Hãy thử như tôi có thể, tôi không thể nhìn thấy một mô hình. Suy nghĩ của bạn?


63
Thuật toán tối ưu về thời gian lập trình viên là tạo ra với hai vòng lặp lồng nhau, sau đó sắp xếp. Tại sao họ lại đặt câu hỏi như thế này?
Tom Zych

21
Bạn có thể xác định các điểm chuyển tiếp bằng cách xem số nào lớn hơn. 2^2 < 5nhưng 2^3 > 5tại thời điểm đó bạn tăng j. Tôi nghĩ bạn có thể tạo đầu ra trong O (n) chứ không phải O (nlgn). @ tom-zynch hai vòng lặp lồng nhau là O (n ^ 2). Câu hỏi này rất hợp lệ
Mikhail

1
Chỉ có một đầu ra, vì vậy giải pháp tối ưu là O (n). Đọc giải pháp của tôi dưới đây
Mikhail

3
Một câu hỏi tương tự đã được giải quyết trước khi rõ ràng: stackoverflow.com/questions/4600048/nth-ugly-number .

1
... và OP có lẽ nên chọn một câu trả lời. Rốt cuộc, anh ấy đã có rất nhiều người tốt.
hủy bỏ

Câu trả lời:


123

Dijkstra có được một giải pháp hùng hồn trong "Kỷ luật lập trình". Anh ta gán vấn đề cho Hamming. Đây là cách tôi thực hiện giải pháp của Dijkstra.

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
Liên kết có liên quan: vi.wikipedia.org/wiki/Regular_number#Alacticms . Tôi không nghĩ rằng đây là một câu hỏi phỏng vấn rất tốt. Đây là một (giấy viết tay) của Dijkstra, nơi ông cung cấp và chứng minh một thuật toán cho vấn đề này: cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF
Elian Ebbing

Khi mục tiêu là "lặp lại trên i và j", bạn cần ít dung lượng lưu trữ hơn, một FIFO là đủ. Xem giải pháp Python của tôi.
GaBorgulya

7
Khi mục tiêu là "lặp lại trên i và j", đó không phải là vấn đề tương tự.
mhum

Đây là một triển khai thực sự tốt đẹp, sử dụng tối thiểu bộ nhớ. Đó là bộ nhớ tuyến tính ngay cả khi bạn chỉ muốn một số.
Thomas Ahle

1
@ThomasAhle Không biết nếu bạn thấy điều này nhưng nó có mã ở cuối có khả năng tính toán số thứ n trong sự cô lập. Ví dụ như một số tiền tỷ .
Will Ness

47

đây là một cách tinh tế hơn để làm điều đó (tinh tế hơn câu trả lời trước đây của tôi, đó là):

hãy tưởng tượng các con số được đặt trong một ma trận:

     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

những gì bạn cần làm là "đi bộ" ma trận này, bắt đầu từ (0,0). Bạn cũng cần theo dõi những bước tiếp theo có thể của bạn. Khi bạn bắt đầu (0,0)bạn chỉ có hai lựa chọn: hoặc (0,1)hoặc (1,0): vì giá trị của (0,1)nhỏ, bạn chọn đó. sau đó làm tương tự cho sự lựa chọn tiếp theo của bạn (0,2)hoặc (1,0). Cho đến nay, bạn có danh sách sau : 1, 2, 4. Bước tiếp theo của bạn là (1,0)vì giá trị ở đó nhỏ hơn (0,3). Tuy nhiên, bây giờ bạn có ba lựa chọn cho bước tiếp theo của mình: (0,3)hoặc (1,1), hoặc (2,0).

Bạn không cần ma trận để có được danh sách, nhưng bạn cần theo dõi tất cả các lựa chọn của mình (tức là khi bạn đạt 125+, bạn sẽ có 4 lựa chọn).


Tôi đã bỏ phiếu này vì tôi đã suy nghĩ theo cùng một dòng, nhưng trong trường hợp chung, đây có phải là một cái gì đó giống như O (i ^ 2 * j) không? Bạn sẽ phải kiểm tra một vài số cho mỗi số bạn xuất ra.
Tom Zych

1
@Tom bạn phải kiểm tra nhiều hơn một số, nhưng điều đó không tệ: khi bạn xuất số từ 125 đến 625, bạn cần xem 4 giá trị. trong khoảng từ 625 đến 3025, bạn nhìn vào 5 giá trị. thực sự, nó sẽ jkiểm tra cho mỗi 1 đầu ra
vlad

+1: Kết hợp với câu hỏi này: stackoverflow.com/questions/5000836/search-alacticm và có vẻ như chúng tôi có giải pháp O (n).

@Moron chết tiệt, tôi không muốn trả 25 đô la cho thuật toán đó, nhưng nó có vẻ thú vị.
vlad

1
thực tế, j ~ n^0.5đối với giá trị thứ n trong một chuỗi, vì ncác giá trị lấp đầy một khu vực trên i x jmặt phẳng. Vì vậy, thuật toán này là O(n^1.5)thời gian, với O(n^0.5)không gian. Nhưng tồn tại một thuật toán thời gian tuyến tính với cùng một không gian n^0.5, và thuật toán heap mini từ câu trả lời dưới đây là O(n*log(n))thời gian với cùng một n^0.5không gian.
Will Ness

25

Sử dụng một đống nhỏ.

Đặt 1.

trích xuất tối thiểu Nói rằng bạn nhận được x.

Đẩy 2x và 5x vào đống.

Nói lại.

Thay vì lưu trữ x = 2 ^ i * 5 ^ j, bạn có thể lưu trữ (i, j) và sử dụng chức năng so sánh tùy chỉnh.


1
Một đống sẽ cung cấp cho lg n thời gian cho các hoạt động của nó, điều này đẩy sự phức tạp lên n lg n.
corsiKa

@glow: Có, tôi không thấy bất kỳ giải pháp O (n) nào được đăng cho đến nay, mặc dù :-)

@abel: Nhận xét đó đã cũ :-) Có vẻ như anh ấy cũng sẽ gặp vấn đề từ (1,1) đến (4,0). Nhưng xem nó như một ma trận trẻ (xem câu trả lời của vlad) thực sự cho phép thuật toán thời gian O (n).

@Moron: Tôi không nghĩ có gì sai với giải pháp đó. Chắc chắn không có gì sai trong 30 yếu tố đầu tiên, mà tôi vừa kiểm tra ngay bây giờ (sẽ bao gồm trường hợp (1,1) -> (4,0)).
hủy bỏ

@abel: Yeah không thực sự cố chạy nó :-) Có lẽ cũng có một bằng chứng dễ dàng về tính đúng đắn của nó. FWIW, nó đã có +1 của tôi.

13

Một giải pháp dựa trên FIFO cần ít dung lượng lưu trữ hơn. Mã 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))

đầu ra:

  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

Điều này rất dễ dàng để làm O(n)trong các ngôn ngữ chức năng. Danh sách lcác 2^i*5^jsố có thể được định nghĩa đơn giản là 1và sau đó 2*lvà được 5*lhợp nhất. Đây là giao diện của 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)

Các mergechức năng cung cấp cho bạn một giá trị mới trong thời gian liên tục. Cũng vậy mapvà do đó cũng vậy l.


Tôi nghĩ rằng 'k' không được xác định
Ither

2
unionthay vào đó, hãy gọi hàm "hợp nhất" này vì nó đang loại bỏ các mục trùng lặp. merge, như một phần của mergesort, phải bảo tồn các bản sao đến từ cả hai chuỗi đầu vào của nó. Xem Data.List.Orderedgói cho những thứ liên quan.
Will Ness

1
+1 cho Data.List.Ordered.union. Điều đó làm cho nó thành một dòng:xs = 1 : union (map (2*) xs) (map (5*) xs)
Phob

@GaBorgulya Vâng, nó bao gồm năm lần danh sách [1, 2, 4, 5,...]để nó bao gồm 5*4.
Thomas Ahle

1
@Phob Vâng, đây là Data.List.Ordered.unionchức năng. Đừng nhầm lẫn với Data.List.union.
Thomas Ahle

5

Bạn phải theo dõi các số mũ riêng lẻ của chúng, và số tiền của chúng sẽ là bao nhiêu

Vì vậy, bạn bắt đầu với f(0,0) --> 1 bây giờ bạn phải tăng một trong số họ:

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

vì vậy chúng tôi biết 2 là tiếp theo - chúng tôi cũng biết rằng chúng tôi có thể tăng số mũ của tôi cho đến khi tổng số vượt quá 5.

Bạn cứ quay đi quay lại như thế này cho đến khi bạn ở số vòng được coi là của bạn.


Vâng, đúng vậy. Bạn thực hiện một thao tác O (1) cho mỗi vòng. Đôi khi bạn thực hiện vòng đấu sớm, nhưng khi bạn đến vòng đó, bạn không cần phải làm điều đó ở đó, vì vậy nó sẽ tự giải quyết.
corsiKa

19
Làm thế nào để bạn đi từ (1,1) đến (4,0)? Hãy giải thích chính xác thuật toán của bạn là gì.

Vấn đề là, bạn không có hai khả năng gia tăng - ví dụ, bạn không thực hiện được f(*,2)chỉ vì bạn thấy điều đó f(a1,b+1)>f(a2,b). Một cách tiếp cận gia tăng cuối cùng sẽ tạo ra một số lượng các cặp không giới hạn lân cận khu vực bạn đã xuất.
sắp tới

@ user515430 cung cấp một triển khai nhiều hơn tôi có thể làm vào giờ nghỉ trưa, nhưng đó là những gì tôi đang cố gắng đạt được.
corsiKa

4

Sử dụng lập trình động, bạn có thể làm điều này trong O (n). Sự thật cơ bản là không có giá trị nào của i và j có thể cho chúng ta 0 và để có được 1 cả hai giá trị phải là 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
    }
}

Bất cứ khi nào bạn gọi hàm này, hãy kiểm tra xem i và j có được đặt không, nếu chúng không rỗng, thì hãy điền TwoCountFiveCount


C ++ trả lời. Xin lỗi vì phong cách mã hóa xấu, nhưng tôi đang vội :(

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

Rõ ràng bạn có thể sử dụng các cấu trúc dữ liệu khác ngoài mảng để tăng động lưu trữ của mình, v.v ... Đây chỉ là một bản phác thảo để chứng minh rằng nó hoạt động.


4
Đây có vẻ là một câu trả lời thú vị, nhưng tôi không thấy nó thực sự hoạt động như thế nào. Bạn có thể thêm chi tiết?
David Brunelle

Sau khi tự nghiên cứu, tôi thực sự không thấy nó hoạt động như thế nào. Giả sử phép chia số nguyên, nó sẽ cho kết quả chính xác tương tự cho 3 như đối với 2. Ngoài ra, nếu điều kiện if là các phép thử khác không, nó sẽ không bao giờ hoạt động, vì không có mục nào khác không.
David Thornley

Đã đăng phiên bản C ++ cho tất cả những người bạn nói này. @David Nhận xét của bạn là chính xác, nhưng mã ban đầu của tôi là mã giả và tôi đã suy nghĩ theo các thuật ngữ kịch bản, do đó, không phân chia số nguyên và phân biệt giữa mục nhập null và mục nhập giá trị 0
Mikhail

mã này liệt kê tất cả các số tự nhiên, vì vậy, theo nhận xét của @ThomasAhle cho câu trả lời của "Lost in Alabama" bên dưới, cần O(exp(sqrt(n)))phải tạo ra các nsố của chuỗi. Thuật toán tuyến tính tồn tại, ví dụ như được đưa ra bởi ThomasAhle.
Will Ness

1
Bạn đúng. Theo hiểu biết của tôi O(n)có nghĩa nlà giá trị cuối cùng, không phải số lượng các mục được in, không đúng. Tôi không biết các ngôn ngữ chức năng hoạt động như thế nào hoặc hợp nhất hoạt động như thế nào trong thời gian liên tục, nhưng câu trả lời của anh ấy đã giúp tôi tăng cường
Mikhail

2

Tại sao không thử nhìn điều này từ hướng khác. Sử dụng một bộ đếm để kiểm tra các câu trả lời có thể so với công thức ban đầu. Xin lỗi cho mã giả.

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

Điều này chạy trong khoảng O(4^sqrt(n))bởi vì nthsố lượng của chuỗi là khoảng kích thước đó.
Thomas Ahle

2

Đây là mục có liên quan tại OEIS.

Dường như có thể có được chuỗi thứ tự bằng cách tạo ra một vài thuật ngữ đầu tiên, nói

1 2 4 5

và sau đó, bắt đầu từ học kỳ thứ hai, nhân 4 và 5 để có hai bài tiếp theo

1 2 4 5 8 10

1 2 4 5 8 10 16 20

1 2 4 5 8 10 16 20 25

và như thế...

Theo trực giác, điều này có vẻ đúng, nhưng tất nhiên là thiếu bằng chứng.


2
Sai :( [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 ] Tuy nhiên 500 <512 = 2 ^ 9 <625
GaBorgulya

1
@NateKerkhofs, 512 được tạo nhưng không theo thứ tự vì 512 nhỏ hơn 625 đã được tạo; thuật toán sẽ cần logic hơn nữa để đưa đầu ra theo thứ tự - Do đó thuật toán không đơn giản như đề xuất và hoàn toàn không phải là thuật toán.
GordonBood

1

Bạn biết rằng log_2 (5) = 2,32. Từ đó, chúng tôi lưu ý rằng 2 ^ 2 <5 và 2 ^ 3> 5.

Bây giờ hãy xem một ma trận các câu trả lời có thể:

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

Bây giờ, cho ví dụ này, chọn các số theo thứ tự. Có thứ tự sẽ là:

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

Lưu ý rằng mỗi hàng bắt đầu 2 cột phía sau hàng bắt đầu. Chẳng hạn, i = 0 j = 1 đến trực tiếp sau i = 2 j = 0.

Do đó, một thuật toán chúng ta có thể rút ra từ mẫu này (giả sử 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);
    }
}   

LƯU Ý: Mã ở đây giới hạn các giá trị của số mũ của i và j nhỏ hơn 10. Bạn có thể dễ dàng mở rộng thuật toán này để phù hợp với bất kỳ giới hạn tùy ý nào khác.

LƯU Ý: Thời gian chạy cho thuật toán này là O (n) cho n câu trả lời đầu tiên.

LƯU Ý: Độ phức tạp không gian cho thuật toán này là O (1)


Bạn đã viết "mỗi hàng bắt đầu 2 cột phía sau hàng bắt đầu". Tuy nhiên 2 ^ 9 = 512 và 5 ^ 4 = 625, vì vậy điều này không đúng với hàng 4.
GaBorgulya

@ user678105 Bạn nói đúng. Mã này không hoạt động. Xin lỗi tất cả. Mã này không hoạt động vì làm tròn nhật ký và giả định của tôi rằng nó không thành vấn đề.
KLee1

1
Đây là cách bạn khắc phục điều này. Trên mặt phẳng (x, y) có đầy đủ các điểm có hệ số tích phân, vẽ một đường thẳng từ (0,1) đến (log2 (5), 0). (0,0) nằm ở góc trên cùng bên trái. Trục X đi bên phải, trục Y đi xuống. Bây giờ, vẽ một đường thẳng từ điểm gốc (0,0) vuông góc với đường thẳng thứ nhất. Bây giờ trượt dòng đầu tiên dọc theo dòng thứ hai, càng ngày càng xa gốc tọa độ và thu thập các điểm tọa độ nguyên khi chúng được giao nhau. Đối với chuỗi được tạo ra {2,3,5}, nó sẽ là một mặt phẳng di chuyển ngang qua, trong không gian (i, j, k). Nếu bạn có thể dịch ý tưởng này thành mã, hãy cho tôi một tiếng hét. :)
Will Ness

1

Việc thực hiện của tôi dựa trên các ý tưởng sau:

  • Sử dụng hai hàng đợi Q2 và Q5, cả hai được khởi tạo với 1. Chúng tôi sẽ giữ cả hai hàng đợi theo thứ tự được sắp xếp.
  • Ở mỗi bước, hãy loại bỏ phần tử số nhỏ nhất MIN từ Q2 hoặc Q5 và in nó. Nếu cả Q2 và Q5 có cùng một yếu tố - hãy loại bỏ cả hai. In số này. Về cơ bản, đây là sự hợp nhất của hai mảng được sắp xếp - ở mỗi bước chọn phần tử nhỏ nhất và tiến lên.
  • Enqueue MIN * 2 đến Q2 và MIN * 5 đến Q5. Thay đổi này không phá vỡ bất biến của Q2 / Q5 đang được sắp xếp, vì MIN cao hơn số MIN trước đó.

Thí dụ:

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

Mã trong 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

tính toán kết quả và đưa chúng vào danh sách được sắp xếp, cùng với các giá trị cho ij


Điều đó có thể sẽ cung cấp cho bạn lỗ hổng ở phần cuối của chuỗi. Ví dụ, bạn sẽ có 2^n*5^nnhưng không 2^(n+1)*5^(n-1)nhỏ hơn.
Thomas Ahle

@Thomas Tôi không chắc chắn tôi làm theo logic của bạn ở đây. Nếu bạn tính toán một, tại sao bạn cũng không tính toán cái kia?
vlad

2
@vlad Bạn cần có một giới hạn đối với bạn ivà bạn j, phải không? Nếu không, bạn sẽ không bao giờ đến trạng thái sắp xếp và do đó bạn sẽ không bao giờ trả lại một giá trị. Nhưng đối với bất kỳ giới hạn nào nbạn chọn, danh sách của bạn sẽ bị thiếu sót.
Thomas Ahle

@Thomas đối số của bạn vẫn không có ý nghĩa. OP không bao giờ chỉ định kết thúc danh sách kết quả của mình. Nếu anh ta làm, bạn có thể tìm thấy tối đa ij .
vlad

1
@vlad Khi tôi đọc câu trả lời của bạn, trước tiên bạn tính "kết quả" / các 2^i*5^jgiá trị, sau đó sắp xếp chúng. Nếu bạn không có số lượng "kết quả" giới hạn, làm thế nào bạn có thể đến bước sắp xếp?
Thomas Ahle

0

Thuật toán được thực hiện bởi user515430 bởi Edsger Dijkstra (http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF) có thể nhanh như bạn có thể nhận được. Tôi gọi mỗi số là một dạng của 2^i * 5^j"số đặc biệt". Bây giờ câu trả lời của vlads sẽ là O(i*j)nhưng với một thuật toán kép, một để tạo ra các số đặc biệt O(i*j)và một để sắp xếp chúng (theo bài báo được liên kết cũng O(i*j).

Nhưng hãy kiểm tra thuật toán của Dijkstra (xem bên dưới). Trong trường hợp nnày là số lượng số đặc biệt chúng tôi đang tạo, do đó, bằng i*j. Chúng tôi đang lặp một lần 1 -> nvà trong mỗi vòng lặp, chúng tôi thực hiện một hành động liên tục. Vì vậy, thuật toán này cũng được O(i*j). Và với một hằng số khá nhanh cũng vậy.

Việc triển khai C ++ của tôi với GMP (trình bao bọc C ++) và sự phụ thuộc vào boost::lexical_cast, mặc dù điều đó có thể dễ dàng loại bỏ (Tôi lười biếng và ai không sử dụng Boost?). Tổng hợp với g++ -O3 test.cpp -lgmpxx -o test. Trên Q6600 Ubuntu 10.10 time ./test 1000000cho 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

Nếu bạn vẽ một ma trận với i là hàng và j là cột, bạn có thể thấy mẫu. Bắt đầu với i = 0 và sau đó chỉ cần duyệt qua ma trận bằng cách đi lên 2 hàng và 1 cột bên phải cho đến khi bạn đạt đến đỉnh của ma trận (j> = 0). Sau đó đi i + 1, v.v ...

Vì vậy, với i = 7 bạn đi du lịch như thế này:

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

Và với i = 8:

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

Ở đây, Java có tới i = 9. Nó in vị trí ma trận (i, j) và giá trị.

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

Trực giác của tôi :

Nếu tôi lấy giá trị ban đầu là 1 trong đó i = 0, j = 0, thì tôi có thể tạo các số tiếp theo là (2 ^ 1) (5 ^ 0), (2 ^ 2) (5 ^ 0), (2 ^ 0) * (5 ^ 1), ... tức là 2,4,5 ..

Hãy nói tại bất kỳ điểm nào số của tôi là x. sau đó tôi có thể tạo các số tiếp theo theo các cách sau:

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

Giải thích :

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.

Chạy thử nghiệm

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]
  • Hãy bắt đầu với x = 1.

    Ba số tiếp theo là 1 * 2, 1 * 4, 1 * 5 [2,4,5]; Arr [1,2,4,5]

  • Bây giờ x = 2

    Ba số tiếp theo là [4,8,10] {Vì 4 đã xảy ra, chúng tôi sẽ bỏ qua nó} [8,10]; Arr [1,2,4,5,8,10]

  • Bây giờ x = 4

    Ba số tiếp theo [8,16,20] {8 đã xảy ra bỏ qua nó} [16,20] Arr [1,2,4,5,8,10,16,20]

  • x = 5

    Ba số tiếp theo [10,20,25] {10,20} đã được thêm [25] Arr [1,2,4,5,8,10,16,20,25]

Điều kiện chấm dứt

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

Phân tích

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

0

Chỉ tò mò những gì mong đợi vào tuần tới và đã tìm thấy câu hỏi này.

Tôi nghĩ rằng, ý tưởng là 2 ^ tôi không tăng trong các bước lớn như 5 ^ j. Vì vậy, tăng tôi miễn là bước tiếp theo j sẽ không lớn hơn.

Ví dụ trong C ++ (Qt là tùy chọn):

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

    }
}

Đầu ra:

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

Giải pháp này bỏ lỡ một số kết hợp. Ví dụ: nó không kiểm tra trường hợp i = 1, j = 2 bất kỳ trường hợp nào trong đó i = 1 và j> 1 cho vấn đề đó ..
Federico

@Federico: Bạn nói đúng! Không có thắc mắc tại sao tôi thất bại trong các cuộc phỏng vấn google hai lần với khoảng thời gian 6 năm nhưng gần như cùng một câu hỏi :-)
Valentin Heinitz

0

Đây là giải pháp của tôi

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

Kết quả :

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

Tôi biết tôi có khả năng sai nhưng có một heuristic rất đơn giản ở đây vì nó không liên quan đến nhiều con số như 2,3,5. Chúng tôi biết rằng với bất kỳ i, j 2 ^ i * 5 ^ j chuỗi tiếp theo sẽ là 2 ^ (i-2) * 5 ^ (j + 1). Là một google q nó phải có một giải pháp đơn giản.

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ều này tạo ra đầu ra như:

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

nó có thể hoạt động tới 20 hoặc 200, nhưng đến một lúc nào đó, nó sẽ bắt đầu bỏ qua một số số và / hoặc xuất chúng theo thứ tự sai.
Will Ness

0

Nếu bạn đi theo những gì thực sự xảy ra khi chúng ta tăng i hoặc j trong biểu thức 2^i * 5^j, bạn sẽ nhân với 2 hoặc khác 5. Nếu chúng ta lặp lại vấn đề như - đưa ra một giá trị cụ thể của i và j, bạn sẽ tìm thấy tiếp theo như thế nào giá trị lớn hơn, giải pháp trở nên rõ ràng.

Dưới đây là các quy tắc chúng ta có thể liệt kê khá trực giác:

  • Nếu có một cặp 2s ( i > 1) trong biểu thức, chúng ta nên thay thế chúng bằng 5 để có được số lớn nhất tiếp theo. Do đó, i -= 2j += 1.
  • Mặt khác, nếu có 5 ( j > 0), chúng ta cần thay thế nó bằng ba 2 giây. Vì vậy j -= 1i += 3.
  • Mặt khác, chúng ta chỉ cần cung cấp thêm 2 để tăng giá trị tối thiểu. i += 1.

Đây là chương trình trong 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ều này không hoạt động vì 'tôi' không bao giờ lớn hơn 4, vì vậy sẽ không có bội số của 32 (2 ^ 5).
threenplusone

0

Nếu chúng tôi được phép sử dụng java Collection thì chúng tôi có thể có những số này trong 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);
}

Ở đây powerLimit phải được khởi tạo rất cẩn thận !! Tùy thuộc vào số lượng bạn muốn.


điều này tạo ra kết quả sai: 2 ^ 8 = 256 bị thiếu trước 2 ^ 6 * 5 = 320. diện tích liệt kê là hình tam giác, không phải hình chữ nhật.
Will Ness

@WillNess thế nào ?? Khi tôi cài đặt powerLimit = 9, đoạn trích này trả về các số sau 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 11/07/2016

không, nó tạo ra 100 số Làm thế nào để bạn biết nơi dừng lại? bạn phải giải thích điều này --- Tôi đã đề cập đến 7 như hiện tại trong đoạn mã của bạn. để đây là một câu trả lời hợp lệ, bạn phải giải thích chính xác cách đặt giới hạn cho một số lượng nhất định và bao nhiêu số sẽ sản xuất quá mức .
Will Ness

0

Đây là nỗ lực của tôi với 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

Đầu ra:

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.