Найти наименьшее число Kth для выражения (2 ^ x) * (3 ^ y) * (5 ^ z)

В выражении

2 x * 3 y * 5 z

x, y и z могут принимать не отрицательное целочисленное значение ( >= 0).

Таким образом, функция будет генерировать ряд чисел 1,2,3,4,5,6,8,9,10,12,15,16....

  • У меня есть решение грубой силы.
  • Я бы в основном перебирал в цикле, начиная с 1 и на каждой итерации я бы нашел, если текущие числовые коэффициенты будут только из набора 2,3 или 5.

То, что я хотел бы иметь, - элегантный алгоритм.

Это вопрос интервью.

Ответ 1

Это можно решить с помощью очереди приоритетов, в которой вы храните триплеты (x, y, z), отсортированные по ключу 2 x 3 y 5 z.

  • Начните с триплета (0, 0, 0) в очереди.

  • Удалите триплет (x, y, z) с наименьшим ключом из очереди.

  • Вставьте три очереди (x + 1, y, z), (x, y + 1, z) и (x, y, z + 1) в очередь. Убедитесь, что вы не вставляете ничего, что уже было там.

  • Повторяйте с шага 2, пока не удалите k триплетов. Последним удаленным является ваш ответ.

По сути, это становится упорядоченным обходом этого направленного ациклического графа. (Первые три уровня, показанные здесь, фактический график, конечно, бесконечен).

infinite graph

Ответ 2

Эта страница отображает решения на языках программирования bazillion. Как обычно, версия Haskell особенно компактна и проста:

hamming = 1 : map (2*) hamming `merge` map (3*) hamming `merge` map (5*) hamming
     where merge (x:xs) (y:ys)
            | x < y = x : xs `merge` (y:ys)
            | x > y = y : (x:xs) `merge` ys
            | otherwise = x : xs `merge` ys

Обновление. Как отметила У. Несс, в Data.List.Ordered есть готовая функция, которая является лучшим выбором, чем мой merge (и у него также есть лучшее имя).

import Data.List.Ordered (union)
hamming = 1 : map (2*) hamming `union` map (3*) hamming `union` map (5*) hamming

Ответ 3

Самое простое решение, о котором я могу думать:

    int[] factors = {2, 3, 5};
    int[] elements = new int[k];
    elements[0] = 1;
    int[] nextIndex = new int[factors.length];
    int[] nextFrom = new int[factors.length];
    for (int j = 0; j < factors.length; j++) {
        nextFrom[j] = factors[j];
    }
    for (int i = 1; i < k; i++) {
        int nextNumber = Integer.MAX_VALUE;
        for (int j = 0; j < factors.length; j++) {
            if (nextFrom[j] < nextNumber) {
                nextNumber = nextFrom[j];
            }
        }
        elements[i] = nextNumber;
        for (int j = 0; j < factors.length; j++) {
            if (nextFrom[j] == nextNumber) {
                nextIndex[j]++;
                nextFrom[j] = elements[nextIndex[j]] * factors[j];
            }
        }
    }
    System.out.println(Arrays.toString(elements));

Это генерирует первые k элементы этого набора в порядке возрастания в O (k) пространстве и времени.

Обратите внимание, что необходимо потреблять nextNumber из всех j, которые предоставляют его для устранения дубликатов (2 * 3 = 3 * 2 в конце концов).

Изменить: Алгоритм использует тот же подход, что и haskell, отправленный n.m.

Ответ 4

Это может быть больше, чем ваши знания алгоритмов, чтобы включать в себя, как вы думаете, решать проблемы и работать в команде.

Перед тем, как начать, важно иметь достойную спецификацию проблемы. Некоторые из неизвестных, как описано, включают:

  • существуют ли границы на K?
  • Вам нужен известный алгоритм или ad-hoc грубая сила?
  • Использование памяти и время вычисления? (может быть, один или другой вопрос)
  • как быстро он должен вычислять vs, сколько времени мне нужно для его разработки?
  • следует ли кэшировать результаты?

Просить интервьюера о некоторых или всех этих вопросах может быть как минимум столь же важным, как возможность ответить на заданный вопрос. Конечно, вы можете рисовать себя в угол таким образом, что может даже быть частью теста....

Ответ 5

Поскольку проблема может быть преобразована в поиск Kth наименьшего числа

 f(x,y,z) = x log(2) + y log(3) + z log(5),

алгоритм может следовать за

  • начинается с f (x, y, z) = f (0,0,0)
  • учитывая текущее наименьшее число f (i, j, k) = v, вы должны найти (x, y, z) такие, что f (x, y, z) является ближайшим к v и > v. Так как

    log(2)<log(3)<2log(2)<log(5)

    Можно сказать, что

    0<=i-2<=x<=i+2, 0<=j-1<=y<=j+1 & 0<=k-1<=z<=k+1 such that f(x,y,z) > v

Итак, так как это нужно найти минимум 45 значений на каждом шаге, и я бы сказал, что это O (K) алгоритм. Конечно, число 45 может быть уменьшено путем наложения большего количества таких условий, как (x, y, z)!= (I, j, k).

Ответ 6

Это номера Хэмминга, которые я использовал в качестве примера в SRFI-41. Это был код, который я использовал там:

(define hamming
  (stream-cons 1
    (stream-unique =
      (stream-merge <
        (stream-map (lsec * 2) hamming)
        (stream-map (lsec * 3) hamming)
        (stream-map (lsec * 5) hamming)))))

Ответ 7

Существует очень элегантное решение этой проблемы. Алгоритм и кодирование просты. Сложность времени - O (n)

Я видел похожую проблему где-то. Задача состояла в том, чтобы сгенерировать числа вида 2 ^ x.3 ^ y в порядке возрастания.

Так вот.

int kthsmallest(int k){

    int two = 0, three = 0, five = 0;
    int A[k];
    A[0] = 1;
    for (int i=1; i<k; i++){
        int min = (A[two] * 2 <= A[three] * 3)? A[two] * 2: A[three] * 3;
        min = (min <= A[five] * 5)? min: A[five] * 5;
        A[i] = min;
        if (min == A[two] * 2)
            two++;
        if (min == A[three] * 3)
            three++;
        if (min == A[five] * 5)
            five++;
    }
    return A[k-1];
}

В основном алгоритм - сохранить три указателя для x, y, z. В коде я использовал два, три и пять. На каждой итерации проверьте, какой из них меньше (2 ^ x, 3 ^ y или 5 ^ z). Поместите это число в i-й индекс и увеличьте соответствующее значение x или y или z. Если значений более одного мин, то увеличивайте оба указателя.

Ответ 8

Ниже приведено рабочее решение на основе java для нахождения k-го наименьшего числа с коэффициентами всего 2,3 и 5. Здесь 2 * 3 * 5 считается наименьшим фактором.

import java.util.Comparator;
import java.util.PriorityQueue;
public class KthSmallestFactor {

    public static void main(String[] args){

        for(int i=1;i<=10;i++){
            System.out.println(kthSmallest(i));
        }
    }

    private static int kthSmallest(int k){
        PriorityQueue<Triplet> p = new PriorityQueue<Triplet>(10, new Comparator<Triplet>() {
            public int compare(Triplet t1, Triplet t2) {
                int score1 = (int) (Math.pow(2, t1.a) * Math.pow(3, t1.b) * Math.pow(5, t1.c)) ; 
                int score2 = (int) (Math.pow(2, t2.a) * Math.pow(3, t2.b) * Math.pow(5, t2.c));
                return score1 -score2;
            }
        });

        p.add(new Triplet(1, 1, 1));
        int count =1;
        while(count <k){
            Triplet top = p.poll();
            count++;
            int a = top.a;
            int b = top.b;
            int c = top.c;
            Triplet t = new Triplet(a+1, b, c);
            if(!p.contains(t)){
                p.add(t);
            }
            t = new Triplet(a, b+1, c);
            if(!p.contains(t)){
                p.add(t);
            }
            t = new Triplet(a, b, c+1);
            if(!p.contains(t)){
                p.add(t);
            }
        }
        Triplet kth = p.poll();
        System.out.println("a: "+kth.a+"b: "+kth.b+"c: "+kth.c);
        return (int) (Math.pow(2, kth.a) * Math.pow(3, kth.b) * Math.pow(5, kth.c));
    }
}
class Triplet{
    int a ;
    int b;
    int c;

    public Triplet(int a , int b, int c){
        this.a = a;
        this.b=b;
        this.c = c;
    }

    public boolean equals(Object other){
        Triplet t = (Triplet)other;
        return this.a== t.a && this.b==t.b && this.c == t.c; 
    }
}

Ответ 9

Начнем с x = y = z = 0; На каждой итерации вычислите три n:

nx = 2^(x+1)*3^y*5^z
ny = 2^x*3^(y+1)*5^z
nz = 2^x*3^y*5^(z+1)

Найдите наименьшее число n среди трех:

n = min(nx, ny, nz).

Увеличьте либо x, y, либо z:

If n == nx -> x = x + 1
If n == ny -> y = y + 1
If n == nz -> z = z + 1

Остановитесь после K-й итерации и верните n.