Найти наименьшее число 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.