คำถามสัมภาษณ์ Google หากิน


169

เพื่อนของฉันกำลังสัมภาษณ์งาน หนึ่งในคำถามสัมภาษณ์ทำให้ฉันคิดว่าแค่ต้องการความคิดเห็น

มีจำนวนเต็ม 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
อัลกอริธึมที่เหมาะสมที่สุดในแง่ของเวลาโปรแกรมเมอร์คือการสร้างด้วยลูปซ้อนกันสองลูปแล้วเรียงลำดับ ทำไมพวกเขาถึงถามคำถามเช่นนี้
Tom Zych

21
คุณอาจสามารถกำหนดจุดเปลี่ยนได้โดยดูจากจำนวนที่มากขึ้น 2^2 < 5แต่2^3 > 5ณ จุดนั้นคุณเพิ่ม j ฉันคิดว่าคุณสามารถสร้างผลลัพธ์ใน O (n) มากกว่า O (nlgn) @ tom-zynch สองลูปซ้อนกันคือ O (n ^ 2) คำถามนี้ถูกต้องมาก
Mikhail

1
มีเอาต์พุตเดียวเท่านั้นทางออกที่ดีที่สุดคือ O (n) อ่านวิธีแก้ปัญหาของฉันด้านล่าง
Mikhail

3
คำถามที่คล้ายกันได้รับการแก้ไขก่อนที่จะเห็นได้ชัดว่า: stackoverflow.com/questions/4600048/nth-ugly-number

1
... และ OP น่าจะเลือกคำตอบแล้ว ท้ายที่สุดเขามีของดีมากมายอยู่แล้ว
abeln

คำตอบ:


123

Dijkstra ได้มาซึ่งวิธีการแก้ปัญหาฝีปากใน "วินัยของการเขียนโปรแกรม" เขากล่าวถึงปัญหาของแฮมมิง นี่คือการใช้งานโซลูชันของ 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
ลิงค์ที่เกี่ยวข้อง: en.wikipedia.org/wiki/Regular_number#Algorithms ฉันไม่คิดว่านี่เป็นคำถามสัมภาษณ์ที่ดีมาก นี่คือ (กระดาษที่เขียนด้วยลายมือ) โดย Dijkstra ที่ซึ่งเขาให้และพิสูจน์อัลกอริทึมสำหรับปัญหานี้: cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF
Elian Ebbing

เมื่อเป้าหมายคือ "การทำซ้ำมากกว่า i และ j" คุณต้องใช้พื้นที่จัดเก็บน้อยกว่า FIFO ก็เพียงพอแล้ว ดูโซลูชัน Python ของฉัน
GaBorgulya

7
เมื่อเป้าหมายคือ "การทำซ้ำฉันและ j" จะไม่เป็นปัญหาเดียวกัน
mhum

นี่เป็นการใช้งานที่ดีมากโดยใช้หน่วยความจำขั้นต่ำ เป็นหน่วยความจำเชิงเส้นแม้ว่าคุณต้องการเพียงหมายเลขเดียวก็ตาม
โทมัส Ahle

1
@ThomasAhle ไม่ทราบว่าคุณเห็นสิ่งนี้หรือไม่แต่มีรหัสในตอนท้ายที่สามารถคำนวณตัวเลขที่แยกได้ เหมือนเช่นbillionth จำนวน
Will Ness

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)คุณมีเพียงสองตัวเลือก: อย่างใดอย่างหนึ่ง(0,1)หรือ(1,0): เนื่องจากค่าของ(0,1)มีขนาดเล็กคุณจึงเลือก แล้วทำเช่นเดียวกันสำหรับทางเลือกของคุณต่อไปหรือ(0,2) เพื่อให้ห่างไกลคุณมีรายการต่อไปนี้:(1,0) 1, 2, 4ย้ายไปของคุณคือตั้งแต่ค่าที่มีขนาดเล็กกว่า(1,0) (0,3)อย่างไรก็ตามขณะนี้คุณมีสามทางเลือกสำหรับการย้ายไปของคุณ: ทั้ง(0,3)หรือหรือ(1,1)(2,0)

คุณไม่จำเป็นต้องใช้เมทริกซ์เพื่อรับรายการ แต่คุณต้องติดตามตัวเลือกทั้งหมดของคุณ (เช่นเมื่อคุณได้รับ 125+ ขึ้นไปคุณจะมี 4 ตัวเลือก)


ฉันโหวตสิ่งนี้เพราะฉันคิดตามบรรทัดเดียวกัน แต่ในกรณีทั่วไปนี่จะไม่ใช่สิ่งที่เหมือนกับ O (i ^ 2 * j) หรือไม่ คุณต้องตรวจสอบหลายหมายเลขสำหรับแต่ละหมายเลขที่คุณส่งออก
Tom Zych

1
@ คุณต้องตรวจสอบมากกว่าหนึ่งหมายเลข แต่ก็ไม่ได้แย่ขนาดนั้น: เมื่อคุณส่งออกหมายเลขระหว่าง 125 ถึง 625 คุณต้องดู 4 ค่า ระหว่าง 625 ถึง 3025 คุณจะดูค่า 5 ค่า ดังนั้นจริง ๆ มันjตรวจสอบทุกเอาต์พุต 1 ครั้ง
vlad

+1: รวมกับคำถามนี้: stackoverflow.com/questions/5000836/search-algorithmและดูเหมือนว่าเรามีโซลูชัน O (n)

@ Moron ยี้ฉันไม่ต้องการจ่าย $ 25 สำหรับอัลกอริทึมนั้น แต่มันดูน่าสนใจ
vlad

1
ที่จริงแล้วj ~ n^0.5สำหรับค่า n-th ในลำดับเนื่องจากnค่าเติมพื้นที่บนi x jระนาบ ดังนั้นอัลโกนี้O(n^1.5)ถึงเวลาพร้อมที่O(n^0.5)ว่าง แต่มีอัลโลเวลาเชิงเส้นที่มีพื้นที่เท่ากัน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
ฮีปจะให้เวลากับการทำงานของแอลจีซึ่งจะเพิ่มความซับซ้อนให้กับเอ็นแอลจีเอ็น
corsiKa

@glow: ใช่ฉันไม่เห็นโซลูชัน O (n) ที่โพสต์ แต่ :-)

@abel: ความคิดเห็นนั้นเก่า :-) ดูเหมือนว่าเขาจะมีปัญหาจาก (1,1) ถึง (4,0) ด้วย แต่การดูว่ามันเป็นเมทริกซ์ของเด็ก (ดูคำตอบของ vlad) จริง ๆ แล้วอนุญาตให้อัลกอริธึม O (n) เวลา

@ Moron: ฉันไม่คิดว่าจะมีอะไรผิดปกติกับวิธีแก้ปัญหานั้น ไม่มีอะไรผิดปกติใน 30 องค์ประกอบแรกซึ่งฉันเพิ่งตรวจสอบตอนนี้ (ซึ่งจะครอบคลุมกรณี (1,1) -> (4,0))
abeln

@abel: ใช่แล้วไม่ได้พยายามเรียกใช้ :-) บางทีอาจมีข้อพิสูจน์เรื่องความถูกต้องของมันเช่นกัน FWIW มันมี +1 ของฉันอยู่แล้ว

13

โซลูชันที่ใช้ FIFO ต้องการพื้นที่จัดเก็บน้อยกว่า รหัสหลาม

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)ในภาษาที่ใช้งานได้ รายการlของ2^i*5^jตัวเลขสามารถกำหนดได้ง่าย ๆ ว่า1แล้ว2*lและ5*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ฟังก์ชั่นช่วยให้คุณมีค่าใหม่ในเวลาคง เพื่อไม่และด้วยเหตุนี้จึงไม่mapl


ฉันคิดว่า 'k' ไม่ได้กำหนดไว้
Ither

2
ลองเรียกฟังก์ชั่น "ผสาน" นี้unionแทนเพราะมันจะลบรายการที่ซ้ำกัน mergeในฐานะส่วนหนึ่งของmergesortจะต้องรักษาความซ้ำซ้อนที่มาจากทั้งสองลำดับการป้อนข้อมูล ดูData.List.Orderedแพ็คเกจสำหรับสิ่งที่เกี่ยวข้อง
Will Ness

1
+1 Data.List.Ordered.unionสำหรับ ที่ทำให้มันเป็นหนึ่งบรรทัด:xs = 1 : union (map (2*) xs) (map (5*) xs)
Phob

@GaBorgulya ใช่มันมีรายการห้าครั้ง[1, 2, 4, 5,...]จึงรวม5*4อยู่ด้วย
โทมัส Ahle

1
@Phob ใช่นี่คือData.List.Ordered.unionฟังก์ชั่น Data.List.unionเพื่อไม่ให้สับสนกับ
โทมัส Ahle

5

คุณต้องติดตามเลขยกกำลังแต่ละอันของพวกเขาและผลรวมของพวกเขาจะเป็นอย่างไร

ดังนั้นคุณเริ่มต้นด้วยf(0,0) --> 1 ตอนนี้คุณต้องเพิ่มอย่างใดอย่างหนึ่ง:

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

ดังนั้นเราจึงรู้ว่า 2 คือถัดไป - เราก็รู้ว่าเราสามารถเพิ่มค่า i แทนได้จนกระทั่งผลรวมเกิน 5

คุณยังคงกลับไปกลับมาเช่นนี้จนกว่าคุณจะอยู่ในจำนวนรอบที่ไม่ถูกต้อง


ใช่แล้ว. คุณดำเนินการหนึ่ง O (1) สำหรับแต่ละรอบ บางครั้งคุณทำรอบก่อน แต่เมื่อคุณไปถึงรอบนั้นคุณไม่จำเป็นต้องทำที่นั่นดังนั้นจึงได้ผล
corsiKa

19
คุณไปจาก (1,1) ถึง (4,0) อย่างไร โปรดอธิบายอย่างละเอียดว่าอัลกอริทึมของคุณคืออะไร

ปัญหาคือคุณไม่ได้มีความเป็นไปได้ที่เพิ่มขึ้นสองอย่างเช่นคุณไม่ได้ทำf(*,2)เพราะคุณพบสิ่งf(a1,b+1)>f(a2,b)นั้น วิธีการที่เพิ่มขึ้นในที่สุดจะสร้างจำนวนคู่ที่ไม่ จำกัด ที่อยู่ใกล้เคียงกับภูมิภาคที่คุณได้ส่งออกไปแล้ว
Comingstorm

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

เมื่อใดก็ตามที่คุณเรียกใช้ฟังก์ชั่นนี้ให้ตรวจสอบว่าฉันและ j ถูกตั้งค่าแล้วหากพวกเขาไม่ใช่โมฆะให้เติม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

หลังจากเรียนด้วยตัวเองฉันไม่เห็นว่ามันทำงานอย่างไร สมมติว่ามีการหารจำนวนเต็มมันจะให้ผลลัพธ์เหมือนกันเป็น 3 สำหรับ 2 นอกจากนี้หากเงื่อนไขคือการทดสอบที่ไม่ใช่ศูนย์มันจะไม่ทำงานเนื่องจากไม่มีรายการที่ไม่ใช่ศูนย์
David Thornley

โพสต์รุ่น C ++ สำหรับทุกคนที่คุณพูดไม่ได้ @David ความคิดเห็นของคุณถูกต้อง แต่รหัสต้นฉบับของฉันคือรหัสหลอกและฉันกำลังคิดในแง่ของการเขียนสคริปต์ดังนั้นการแบ่งจำนวนเต็มและการแยกระหว่างรายการว่างและรายการค่า 0
Mikhail

รหัสนี้จะแจกแจงจำนวนธรรมชาติทั้งหมดดังนั้นต่อความคิดเห็นของ @ThomasAhle ต่อคำตอบโดย "Lost in Alabama" ด้านล่างจะใช้O(exp(sqrt(n)))เพื่อสร้างnตัวเลขของลำดับ มีอัลกอริทึมแบบเชิงเส้นเช่นตามที่ ThomasAhle ให้ไว้
Will Ness

1
คุณถูก. ในความเข้าใจของฉันO(n)หมายถึงnค่าสุดท้ายไม่ใช่จำนวนรายการที่พิมพ์ซึ่งไม่ถูกต้อง ฉันไม่ทราบว่าภาษาที่ใช้งานได้ทำงานอย่างไรหรือผสานรวมอย่างไรในเวลาที่คงที่ แต่คำตอบของเขาทำให้ฉันได้รับการโหวต
มิคาอิล

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หมายเลขของลำดับมีขนาดประมาณนั้น
โทมัส Ahle

2

นี่คือรายการที่เกี่ยวข้องที่ OEIS

ดูเหมือนว่าเป็นไปได้ที่จะได้รับการเรียงลำดับโดยการสร้างคำศัพท์สองสามคำแรก

1 2 4 5

จากนั้นเริ่มจากเทอมที่สองคูณด้วย 4 และ 5 เพื่อให้ได้สองเทอมถัดไป

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 <512 = 2 ^ 9 <625.
GaBorgulya

1
@NateKerkhofs, 512 ถูกสร้างขึ้น แต่มันผิดปกติเนื่องจาก 512 น้อยกว่า 625 ที่สร้างไว้แล้ว อัลกอริทึมจะต้องใช้ตรรกะเพิ่มเติมเพื่อวางผลลัพธ์ตามลำดับดังนั้นอัลกอริทึมจึงไม่ง่ายอย่างที่เสนอและไม่ใช่อัลกอริทึมเดียวกันเลย
GordonBGood

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 คุณสามารถขยายอัลกอริทึมนี้เพื่อให้พอดีกับขอบเขตอื่น ๆ โดยพลการ

หมายเหตุ: เวลาทำงานสำหรับอัลกอริทึมนี้คือ O (n) สำหรับคำตอบแรก 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 ลดลง ตอนนี้วาดเส้นจากจุดกำเนิด (0,0) ซึ่งตั้งฉากกับบรรทัดที่ 1 ตอนนี้เลื่อนบรรทัดแรกไปตามจุดที่สองเพิ่มขึ้นและไกลออกไปจากจุดกำเนิดและรวบรวมจุดจำนวนเต็มขณะที่ข้าม สำหรับ {2,3,5} - ลำดับที่สร้างขึ้นมันจะเป็นระนาบที่เคลื่อนที่ข้ามในพื้นที่ (i, j, k) หากคุณสามารถแปลความคิดนี้เป็นรหัสให้ฉันตะโกนออกมา :)
Will Ness

1

การนำไปใช้ของฉันขึ้นอยู่กับแนวคิดต่อไปนี้:

  • ใช้สองคิว Q2 และ Q5 ทั้งสองเริ่มต้นด้วย 1 เราจะเก็บทั้งสองคิวในลำดับที่เรียง
  • ในทุกขั้นตอน dequeue องค์ประกอบจำนวนที่เล็กที่สุด MIN จาก Q2 หรือ Q5 และพิมพ์ หากทั้ง Q2 และ Q5 มีองค์ประกอบเดียวกัน - ลบทั้งคู่ พิมพ์หมายเลขนี้ นี่คือการผสานสองอาร์เรย์ที่เรียงลำดับ - ในแต่ละขั้นตอนเลือกองค์ประกอบที่เล็กที่สุดและล่วงหน้า
  • Enqueue MIN * 2 ถึง Q2 และ MIN * 5 ถึง Q5 การเปลี่ยนแปลงนี้ไม่ทำลายค่าคงที่ของ Q2 / Q5 ที่เรียงลำดับเนื่องจาก MIN สูงกว่าค่า MIN ก่อนหน้า

ตัวอย่าง:

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^nแต่ไม่2^(n+1)*5^(n-1)เล็กกว่า
โทมัส Ahle

@ โทมัสฉันไม่แน่ใจว่าฉันทำตามตรรกะของคุณที่นี่ หากคุณคำนวณหนึ่งทำไมคุณไม่คำนวณอีกด้วย
vlad

2
@vlad คุณจำเป็นต้องมีขีด จำกัด บนของคุณi'และj' s, คุณไม่? มิฉะนั้นคุณจะไม่ได้รับสถานะการเรียงลำดับดังนั้นคุณจะไม่คืนค่าใด ๆ เลย แต่สำหรับข้อ จำกัด ที่nคุณเลือกรายการของคุณจะมีข้อบกพร่อง
โทมัส Ahle

@ โทมัสข้อโต้แย้งของคุณยังไม่สมเหตุสมผล OP ไม่เคยระบุจุดสิ้นสุดในรายการผลลัพธ์ของเขา ถ้าเขาทำคุณสามารถหาสูงสุดijและ
vlad

1
@vlad เมื่อฉันอ่านคำตอบของคุณคุณต้องคำนวณ "ผลลัพธ์" / 2^i*5^jค่าก่อนแล้วจึงเรียงลำดับ หากคุณไม่มี "ผลลัพธ์" จำนวน จำกัด คุณจะไปยังขั้นตอนการเรียงลำดับได้อย่างไร
โทมัส Ahle

0

อัลกอริทึมที่นำมาใช้โดย user515430 โดย Edsger Dijkstra (http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF) น่าจะเร็วที่สุดเท่าที่จะทำได้ ฉันเรียกทุกหมายเลขที่เป็นรูปแบบของ2^i * 5^j"หมายเลขพิเศษ" ตอนนี้คำตอบ vlads จะเป็นO(i*j)แต่ด้วยอัลกอริทึมสองหนึ่งในการสร้างหมายเลขพิเศษO(i*j)และหนึ่งในการจัดเรียงพวกเขา (ตามบทความที่เชื่อมโยงO(i*j)ด้วย

แต่มาตรวจสอบอัลกอริทึมของ Dijkstra (ดูด้านล่าง) ในกรณีนี้คือจำนวนตัวเลขที่พิเศษที่เราจะสร้างเพื่อให้เท่ากับn i*jเราวนลูปหนึ่งครั้ง1 -> nและในทุกลูปเราทำการกระทำที่คงที่ ดังนั้นอัลกอริทึมนี้ก็เช่นO(i*j)กัน และด้วยค่าคงที่ที่เห็นได้ชัดอย่างรวดเร็วเช่นกัน

การนำไปใช้ของฉันใน C ++ ด้วย GMP (C ++ wrapper) และการพึ่งพาboost::lexical_castแม้ว่าจะสามารถลบได้อย่างง่ายดาย (ฉันขี้เกียจและผู้ที่ไม่ใช้ Boost?) g++ -O3 test.cpp -lgmpxx -o testรวบรวมกับ ใน Q6600 Ubuntu 10.10time ./test 10000001145msจะช่วยให้

#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

หากคุณวาดเมทริกซ์โดยที่ฉันเป็นแถวและ j เป็นคอลัมน์คุณจะเห็นรูปแบบ เริ่มต้นด้วย i = 0 จากนั้นแค่เลื่อนเมทริกซ์โดยขึ้นไป 2 แถวและ 1 คอลัมน์ทางขวาจนกว่าคุณจะถึงด้านบนของเมทริกซ์ (j> = 0) จากนั้นไปที่ i + 1 ฯลฯ ...

ดังนั้นสำหรับ i = 7 คุณเดินทางแบบนี้:

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

และสำหรับฉัน = 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

สัญชาติญาณของฉัน :

ถ้าฉันใช้ค่าเริ่มต้นเป็น 1 โดยที่ i = 0, j = 0 จากนั้นฉันสามารถสร้างตัวเลขถัดไปเป็น (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

    หมายเลขสามหมายเลขถัดไปคือ 1 * 2, 1 * 4, 1 * 5 [2,4,5]; arr [1,2,4,5]

  • ตอนนี้ x = 2

    ตัวเลขสามหมายเลขถัดไปคือ [4,8,10] {ตั้งแต่เกิดขึ้น 4 เราจะไม่สนใจมัน} [8,10]; arr [1,2,4,5,8,10]

  • ตอนนี้ x = 4

    หมายเลขสามหมายเลขถัดไป [8,16,20] {8 เกิดขึ้นแล้วไม่สนใจ} [16,20] Arr [1,2,4,5,8,10,16,20]

  • x = 5

    สามหมายเลขถัดไป [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

เพิ่งสงสัยว่าจะคาดหวังอะไรในสัปดาห์หน้าและได้พบคำถามนี้

ฉันคิดว่าความคิดคือ 2 ^ ฉันไม่ได้เพิ่มในขั้นตอนที่ยิ่งใหญ่เช่น 5 ^ j ดังนั้นเพิ่ม i ตราบใดที่ j-step ถัดไปจะไม่ใหญ่ขึ้น

ตัวอย่างใน 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

วิธีนี้ไม่ได้รวมกัน ตัวอย่างเช่นจะไม่ตรวจสอบกรณีที่ i = 1, j = 2 กรณีใด ๆ ที่ i = 1 และ j> 1 สำหรับเรื่องนั้น ..
Federico

@Federico: ถูกต้อง! ไม่น่าแปลกใจเลยว่าทำไมฉันถึงสัมภาษณ์ google ล้มเหลวสองครั้งด้วยช่วงเวลา 6 ปี แต่เกือบจะเป็นคำถามเดียวกัน :-)
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) การเป็น google 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 แต่ในบางจุดมันจะเริ่มข้ามตัวเลขบางส่วนและ / หรือส่งออกในลำดับที่ไม่ถูกต้อง
Will Ness

0

ถ้าคุณไปด้วยสิ่งที่เกิดขึ้นจริงเมื่อเราเพิ่ม i หรือ j ในนิพจน์2^i * 5^jคุณจะคูณด้วยอีก 2 หรืออีก 5 ถ้าเราทบทวนปัญหาเป็น - ให้ค่าเฉพาะของ i และ j คุณจะหาค่าต่อไปได้อย่างไร คุณค่าที่มากขึ้นการแก้ปัญหาจะชัดเจน

นี่คือกฎที่เราสามารถระบุได้โดยสังหรณ์ใจ:

  • หากมีคู่ของ 2s ( i > 1) ในการแสดงออกเราควรแทนที่พวกเขาด้วย 5 เพื่อรับจำนวนมากที่สุดต่อไป ดังนั้นและi -= 2j += 1
  • มิฉะนั้นหากมี 5 ( j > 0) เราต้องแทนที่ด้วยสาม 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

0

หากเราได้รับอนุญาตให้ใช้ java Collection เราสามารถมีหมายเลขเหล่านี้เป็น 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 80 100 125 128 160 200 250 256 320 400 500
kavi temer

ไม่มันผลิตได้ 100 ตัวเลข คุณจะรู้ได้อย่างไรว่าจะหยุดที่ไหน คุณต้องอธิบายเรื่องนี้ --- ฉันอ้างถึง 7 ว่ามีอยู่ในข้อมูลโค้ดของคุณ เพื่อให้เป็นคำตอบที่ถูกต้องคุณจะต้องอธิบายวิธีการกำหนดวงเงินสำหรับจำนวนที่กำหนดและจำนวนที่จะได้รับเกินจำนวนนั้น
Will Ness

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.