Хитрі питання щодо інтерв'ю 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
Оптимальний алгоритм з точки зору програмістського часу полягає в генерації з двома вкладеними циклами, а потім сортування. Чому вони задають такі питання?
Том Зіч

21
Ви можете визначити точки переходу, подивившись, яке число більше. 2^2 < 5але 2^3 > 5так у цей момент ви збільшуєте j. Я думаю, ви можете отримати вихід у O (n), а не O (nlgn). @ tom-zynch дві вкладені петлі - це O (n ^ 2). Це питання є дуже актуальним
Михайло

1
Є лише один вихід, тому оптимальним рішенням є O (n). Прочитайте моє рішення нижче
Михайло

3
Подібне питання було вирішено і раніше: stackoverflow.com/questions/4600048/nth-ugly-number .

1
... і ОП, ймовірно, вже повинен вибрати відповідь. Зрештою, у нього вже багато хороших.
abeln

Відповіді:


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 . Я не думаю, що це, до речі, запитання про інтерв'ю. Ось (рукописний папір) від Dijkstra, де він забезпечує і доводить алгоритм цієї проблеми: cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF
Еліан

Коли мета - "повторити і i j", вам потрібна менша ємність для зберігання, FIFO достатньо. Дивіться моє рішення Python.
ГаБоргуля

7
Коли мета - "повторити над i та j", це не та сама проблема.
mhum

Це дійсно приємна реалізація, використовуючи мінімум пам'яті. Це лінійна пам'ять, навіть якщо вам потрібно лише одне число.
Томас Ейл

1
@ThomasAhle Не знаю, чи ви це бачили, але в кінці є код, який може обчислити n-е число в ізоляції. Наприклад, мільярдне число .
Буде Несс

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)? Вам доведеться перевірити кілька номерів для кожного виведеного номера.
Том Зич

1
@Тому вам доведеться перевірити більше одного числа, але це не так вже й погано: коли ви виводите цифри між 125 і 625, вам потрібно переглянути 4 значення. між 625 і 3025, ви дивитесь на 5 значень. так справді, це jперевірки на кожен 1 вихід
vlad

+1: Поєднайте це питання: stackoverflow.com/questions/5000836/search-algorithm і виглядає так, що у нас є рішення O (n).

@Moron darn, я не хочу платити $ 25 за цей алгоритм, але це виглядає цікаво.
влада

1
власне, j ~ n^0.5для n-го значення в послідовності, оскільки nзначення заповнюють i x jплощину на площині. Отже, це альго - це O(n^1.5)час, з O(n^0.5)простором. Але існує лінійне альго часу з таким самим простором n^0.5, і міні-купа альго з відповіді нижче - це O(n*log(n))час з тим же n^0.5простором.
Буде Несс

25

Використовуйте міні-купу.

Покладіть 1.

витяг-Мін. Скажіть, ви отримуєте х.

Натисніть 2x і 5x у купу.

Повторіть.

Замість того, щоб зберігати x = 2 ^ i * 5 ^ j, ви можете зберігати (i, j) і використовувати спеціальну функцію порівняння.


1
Купа дасть lg n час на свої операції, що підштовхує складність до n lg n.
corsiKa

@glow: Так, поки що я не бачу жодних O (n) рішень, однак :-)

@abel: Цей коментар старий :-) Схоже, у нього також будуть проблеми з (1,1) до (4,0). Але розглядати його як матрицю Юнга (див. Відповідь Влада) насправді дозволяє алгоритм часу O (n).

@Moron: Я не думаю, що в цьому рішенні нічого поганого. Безумовно, нічого поганого в перших 30 елементах, які я зараз перевірив (що охоплює випадок (1,1) -> (4,0)).
abeln

@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)на функціональних мовах. Список 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Функція дає нове значення в постійна час. Так mapі це робить l.


Я думаю, що "k" не визначено
Ither

2
давайте просто назвемо цю функцію "злиття" unionзамість цього, оскільки вона видаляє дублікати. merge, як частина mergesort, має зберігати дублікати, що надходять з обох вхідних послідовностей. Дивіться Data.List.Orderedпакет для пов'язаних матеріалів.
Буде Несс

1
+1 для Data.List.Ordered.union. Це робить його одним рядком:xs = 1 : union (map (2*) xs) (map (5*) xs)
Phob

@GaBorgulya Так, цей список містить п'ять разів, [1, 2, 4, 5,...]так що він включає 5*4.
Томас Ейл

1
@Phob Так, це Data.List.Ordered.unionфункція. Не плутати з Data.List.union.
Thomas 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). Поступовий підхід врешті-решт створить необмежену кількість пар сусіднього регіону, який ви вже отримали.
Буря,

@ 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 i 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
Це виглядає як цікава відповідь, але я не бачу, як це насправді працює. Чи можете ви додати більше деталей?
Девід Брунель

Вивчивши його, я дійсно не бачу, як це працює. Якщо припустити поділ на цілі числа, це дасть абсолютно такий самий результат для 3, як і для 2. Більше того, якщо умови, якщо умови є тестами для ненульових, це ніколи не буде працювати, оскільки немає ненульових записів.
Девід Торнлі

Опублікував версію на C ++ для всіх, хто говорить вас. @David Ваші коментарі правильні, але моїм оригінальним кодом був псевдо-код, і я думав у сценарії термінів, тому не ціле поділ і розмежування між нульовим записом та введенням значення 0
Михайло

цей код перераховує всі натуральні числа, тому за коментарем @ThomasAhle на відповідь "Загублений в Алабамі" нижче, це потрібно O(exp(sqrt(n))), щоб створити nномери послідовності. Лінійний алгоритм існує, наприклад, заданий ThomasAhle.
Буде Несс

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кількість послідовностей приблизно такого розміру.
Томас Ейл

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.

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.
ГаБоргуля

@ user678105 Ви праві. Цей код не працює. Вибачте всіх. Цей код не працює через округлення журналу та моєму припущенні, що це не мало значення.
KLee1

1
Ось як це виправити. На площині (x, y), повної точок з інтегральними коефіцієнтами, проведемо лінію від (0,1) до (log2 (5), 0). (0,0) знаходиться у верхньому лівому куті. Вісь X йде вправо, вісь Y йде вниз. Тепер проведемо лінію з точки початку (0,0), яка перпендикулярна 1-й лінії. Тепер просуньте перший рядок по другому, далі та далі від початку, і збирайте цілочисельні координати, коли вони перетинаються. Для {2,3,5} -породженої послідовності це буде площина, що рухається поперек, у (i, j, k) просторі. Якщо ви можете перекласти цю ідею в код, дайте мені кричати. :)
Буде Несс

1

Моя реалізація базується на таких ідеях:

  • Використовуйте дві черги Q2 та Q5, обидві ініціалізовані з 1. Ми збережемо обидві черги у відсортованому порядку.
  • На кожному кроці видаліть з Q2 або Q5 найменший числовий елемент MIN та надрукуйте його. Якщо і Q2, і Q5 мають один і той же елемент - видаліть обидва. Роздрукуйте цей номер. Це в основному злиття двох відсортованих масивів - на кожному кроці вибирайте найменший елемент і просувайтесь.
  • Запишіть 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)який менший.
Томас Ейле

@Thomas Я не впевнений, що я дотримуюся вашої логіки. Якщо ви обчислюєте одне, чому б ви також не обчислили інше?
влада

2
@vlad Потрібно мати обмеження на ваші iі та j, чи не так? Інакше ви ніколи не дістанетесь до стану сортування, а значить, ніколи не повернете жодного значення. Але за будь-якого обраного nвами обмеження ваш список буде недосконалим.
Thomas Ahle

@Томас ваш аргумент досі не має сенсу. ОП ніколи не визначав припинення свого списку результатів. Якщо він це зробить, ви можете знайти максi і j.
vlad

1
@vlad Коли я читаю вашу відповідь, ви спочатку обчислюєте "результати" / 2^i*5^jзначення, а потім сортуєте їх. Якщо у вас немає обмеженої кількості "результатів", як ви коли-небудь перейдете до етапу сортування?
Thomas Ahle

0

Алгоритм, реалізований користувачем515430 Edsger Dijkstra (http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF), ймовірно, такий швидкий, наскільки ви можете отримати. Я називаю кожне число, яке є формою 2^i * 5^j"спеціального номера". Тепер відповісти власників було б, O(i*j)але з подвійним алгоритмом, один для створення спеціальних чисел O(i*j)і один для їх сортування (відповідно до пов'язаної статтіO(i*j) .

Але давайте перевіримо алгоритм Дейкстри (див. Нижче). У цьому випадку nкількість спеціальних чисел, які ми генеруємо, настільки дорівнює i*j. Ми циклічаємо один раз, 1 -> nі в кожній петлі виконуємо постійну дію. Тож цей алгоритм також єO(i*j) . І з досить палаючим швидким постійним теж.

Моя реалізація в C ++ з GMP (C ++ обгорткою), і залежність від boost::lexical_cast, хоча це можна легко усунути (я лінивий, а хто не використовує Boost?). Укладено з g++ -O3 test.cpp -lgmpxx -o test. У Q6600 Ubuntu 10.10time ./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, а потім просто пройдіть матрицю, піднявшись вгору на 2 ряди та вправо на 1 стовпець, поки не досягнете вершини матриці (j> = 0). Потім перейдіть 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

Моя інтуїція :

Якщо я приймаю початкове значення як 1, де i = 0, j = 0, то наступні числа я можу створити як (2 ^ 1) (5 ^ 0), (2 ^ 2) (5 ^ 0), (2 ^ 0) * (5 ^ 1), ... тобто 2,4,5 ..

Скажімо, в будь-якій точці моє число x. то я можу створити наступні числа такими способами:

  • х * 2
  • х * 4
  • х * 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]; Зар. [1,2,4,5]

  • Тепер х = 2

    Наступні три числа - [4,8,10] {Оскільки 4 вже відбулися, ми проігноруємо його} [8,10]; Зар [1,2,4,5,8,10]

  • Тепер х = 4

    Наступні три числа [8,16,20] {8 вже відбулося ігноруйте його} [16,20] Arr [1,2,4,5,8,10,16,20]

  • х = 5

    Наступні три числа [10,20,25] {10,20} вже так [25] додано Арр [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-крок не буде більшим.

Приклад в 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: Ви праві! Не дивно, чому я два рази провалював google-інтерв'ю з інтервалом у 6 років, але майже з тими ж питаннями :-)
Валентин Хайніц

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, але в якийсь момент він почне пропускати деякі цифри та / або виводити їх у неправильному порядку.
Буде Несс

0

Якщо ви переходите до того, що насправді відбувається, коли ми збільшуємо i або j у виразі 2^i * 5^j , ви або множимо на ще 2, або ще 5. Якщо ми перезапустимо проблему як - з огляду на певне значення i та j, як би ви знайшли наступне більша цінність, рішення стає очевидним.

Ось правила, які ми можемо досить інтуїтивно перерахувати:

  • Якщо i > 1в виразі є пара 2s ( ), нам слід замінити їх на 5, щоб отримати наступне найбільше число. Таким чином, i -= 2і j += 1.
  • В іншому випадку, якщо є 5 ( j > 0), нам потрібно замінити його на три 2. Так j -= 1і i += 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

Це не працює, оскільки "я" ніколи не стає більшим, ніж 4, тому жодне кратне число 32 (2 ^ 5) ніколи не з’явиться.
threenplusone

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. область перерахування трикутна, а не прямокутна.
Буде Несс

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

ні, він видає 100 чисел. як ти знаєш, де зупинитися? ви повинні пояснити це. --- У вашому фрагменті коду я вказав 7. щоб це було правильною відповіддю, ви повинні точно пояснити, як встановити ліміт для заданої кількості чисел і скільки номерів буде перевищувати .
Буде Несс

0

Ось моя спроба зі Скалою:

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.