Encuentre el K-ésimo número mínimo para la expresión (2 ^ x) * (3 ^ y) * (5 ^ z)

En la expresion

2x * 3y * 5z

La x, y y z puede tomar un valor entero no negativo (> = 0).

Entonces la función generaría una serie de números 1,2,3,4,5,6,8,9,10,12,15,16....

  • Tengo una solución de fuerza bruta.
  • Básicamente, iteraría en un ciclo comenzando con 1 y en cada iteración encontraría si los factores numéricos actuales son solo del conjunto de 2,3 o 5.

Lo que me gustaría tener es un algoritmo elegante.

Esta es una pregunta de entrevista.

preguntado el 27 de agosto de 11 a las 14:08

¿Podrías reescribir la expresión usando una sintaxis más clara, tal vez con algunos elementos ? -

Hmm, estoy bastante seguro de que vi una pregunta similar en SO, que trata solo con 2 ^ x * 5 ^ y. Pero no puedo encontrarlo ahora. Creo que esa también fue una pregunta de entrevista. -

La solución prioritaria es buena, pero creo que debería aceptarse una de las soluciones O (n). -

9 Respuestas

Esto se puede resolver usando una cola de prioridad, donde almacena trillizos (x, y, z) ordenados por la clave 2x3y5z.

  1. Empiece solo con el triplete (0, 0, 0) en la cola.

  2. Quita el triplete (x, y, z) con la clave más pequeña de la cola.

  3. Inserta los tres trillizos (x + 1, y, z), (x, y + 1, z) y (x, y, z + 1) en la cola. Asegúrese de no insertar nada que ya estuviera allí.

  4. Repita desde el paso 2 hasta que haya eliminado k trillizos. El último eliminado es tu respuesta.

En efecto, esto se convierte en un recorrido ordenado de este gráfico acíclico dirigido. (Los primeros tres niveles se muestran aquí, el gráfico real es, por supuesto, infinito).

grafo infinito

Respondido 28 ago 11, 05:08

Eso no funcionará porque, por ejemplo, 2 ^ 2 = 4 viene antes de 5 ^ 1 = 5 - Yochai Timmer

@Yochai, funcionará, porque la solución usa lista de prioridades cola. - svick

Así que define la prioridad como el resultado más bajo de los tripletes ... ok, y recuerde qué combinación le dio el resultado para que pueda agregar los siguientes tres tripletes ... - Yochai Timmer

Esa solución toma O (k log k) tiempo, porque la cola de prioridad alcanzará el tamaño O (k). Mi solución es más rápida :-) - meriton

@hammar puede verificar si hay duplicados con una búsqueda binaria en O (ln n), que es el mismo costo que insertar en una cola de prioridad, por lo que no cambia la complejidad algorítmica. - Dijkstra

En esta página enumera soluciones en millones de lenguajes de programación. Como de costumbre, la versión Haskell es particularmente compacta y sencilla:

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

Noticias Como ha señalado Will Ness, hay una función predefinida en Data.List.Ordered que es una mejor eleccion que mi merge (y también tiene un nombre mejor).

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

Respondido 16 Abr '12, 19:04

La pereza hace que esto sea bastante elegante. - Hammar

La 'versión alternativa que usa "iteradores cíclicos"' es una solución de Python muy bonita para cualquiera que decida qué solución de Python leer. - Neil G

Esta función de fusión de eliminación de duplicados se llama union ahora. Está dentro Data.List.Ordered paquete. El nombre merge debe dejarse para la variante de conservación de duplicados, como parte de mergesort. - Will Ness

@NeilG se parece al de Python tee() La función utilizada en los "iteradores cíclicos" crea tres copias de la secuencia, cada una consumida a su propio ritmo, a diferencia de Haskell, que utiliza almacenamiento compartido para los tres. - Will Ness

La solución más sencilla que se me ocurre:

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

Esto genera el primer k elementos de ese conjunto en orden ascendente en O (k) espacio y tiempo.

Tenga en cuenta que es necesario consumir nextNumber desde todos j que lo proporcionan para eliminar duplicados (2 * 3 = 3 * 2 después de todo).

Editar: el algoritmo utiliza el mismo enfoque que el de haskell publicado por nm

Respondido el 18 de Septiembre de 12 a las 00:09

esto es en realidad la respuesta correcta a la pregunta aquí (así como el código de Haskell, pero esto está en Java, como se le preguntó). Solo hice una pequeña mejora en el código allí, correspondiente al pseudocódigo en stackoverflow.com/a/10160054/849891 . - Will Ness

esto en realidad corresponde a la código original de Edsger Dijkstra. - Will Ness

Esto podría ser probar más que tu conocimiento de algoritmos, para incluir cómo piensas, resuelves problemas y trabajas en equipo.

Es importante tener una especificación decente del problema antes de comenzar. Algunas de las incógnitas, como se describe, incluyen:

  • ¿Hay límites en K?
  • ¿Quieres un algoritmo conocido o la fuerza bruta ad-hoc está bien?
  • uso de memoria vs tiempo de computación? (tal vez uno u otro asunto)
  • ¿Qué tan rápido tiene que calcular frente a cuánto tiempo tengo para desarrollarlo?
  • ¿Deberían almacenarse en caché los resultados?

Preguntar al entrevistador sobre algunas o todas estas preguntas puede ser al menos tan importante como poder responder a la pregunta formulada. Por supuesto, puedes meterte en un rincón de esta manera, lo que incluso puede ser parte de la prueba ...

Respondido 27 ago 11, 19:08

+1 ... Tienes razón en el lugar. Lo que me hace reír todo el tiempo en estas "preguntas de la entrevista" es la falta de especificaciones, lo que suele hacer que la pregunta sea totalmente estúpida. Es por eso que los problemas declarados como los de TopCoder o SPOJ son simplemente taaaan mucho mejor que la mayoría de preguntas de entrevista estúpidas que se les ocurren a los entrevistadores estúpidos (y, sí, he estado realizando una entrevista y, sí, parecían preguntas de TopCoder o SPOJ;) - Sintaxis T3rr0r

Dado que el problema se puede convertir para encontrar el K-ésimo número mínimo de

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

el algoritmo podría estar siguiendo

  1. comienza con f (x, y, z) = f (0,0,0)
  2. dado el número mínimo actual f (i, j, k) = v, debes encontrar (x, y, z) tal que f (x, y, z) sea lo más cercano a vy> v. Dado que

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

    Podemos decir

    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

Entonces, dado que esto es para encontrar el mínimo de 45 valores en cada paso, yo diría que es el algoritmo O (K). Por supuesto, el número 45 se puede reducir imponiendo más condiciones como (x, y, z)! = (I, j, k).

Respondido 27 ago 11, 21:08

esto está mal, aunque pensar en la dirección correcta (hay is una solución local para esto, que todavía no he dominado yo mismo). Para ver por qué está mal, considere el número 2^64 correspondiente a la tupla (64,0,0)y sus vecinos. La diferencia en (i,j,k) será mucho más de 3 o 5. - Will Ness

Estos son el Números de Hamming, que utilicé como ejemplo en SRFI-41. Este fue el código que usé allí:

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

respondido 14 nov., 11:06

sólo tangencialmente relacionados, los duplicados-preservando stream-merge se puede (¿debería?) cambiarse fácilmente, con un pequeño ajuste, en una eliminación de duplicados stream-union, De modo que stream-unique la llamada no será necesaria en absoluto. - Will Ness

Existe una solución muy elegante para este tipo de problema. El algoritmo y la codificación son simples. La complejidad del tiempo es O (n)

Vi un problema similar en alguna parte. El problema era generar los números de la forma 2 ^ x.3 ^ y en orden ascendente.

Así que aquí va.

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

El algoritmo es básicamente: mantenga tres punteros para x, y, z. En el código, usé Digital XNUMXk, Digital XNUMXk y Digital XNUMXk. En cada iteración, verifique cuál más pequeña (2 ^ x, 3 ^ años or 5 ^ z). Pon ese número en el ITH indexar e incrementar el valor correspondiente de x or y or z. Si hay más de un valor mínimo, incremente ambos punteros.

Respondido 20 Abr '12, 11:04

A continuación se muestra una solución funcional basada en Java para encontrar el k-ésimo número más pequeño que tiene factores de solo 2,3 y 5. Aquí 2 * 3 * 5 se considera como el factor más pequeño.

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

Respondido el 22 de junio de 15 a las 21:06

Empiece con x = y = z = 0; En cada iteración, calcule tres 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)

Encuentra el mínimo n entre los tres:

n = min(nx, ny, nz).

Aumente x, y o z:

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

Deténgase después de la K-ésima iteración y devuelva n.

Respondido 27 ago 11, 19:08

De esta manera, solo generaría números en la forma 2^x. Incrementando x siempre hace un número menor que incrementar y or z. - svick

No creo que esto funcione, mira de 8 a 9. 8 = 2 ^ 3 y 9 = 3 ^ 2 .. tendrías que encontrar 2 ^ 4. (¿O me falta algo?) - Ricky Bobby

Parece una solución incorrecta. En la segunda iteración, tengo x = 1, y = 0, z = 0. Ahora en la tercera iteración, nx = 4, ny = 6, nz = 10. El menor es 4 (nx). Pero aquí el valor esperado debería haber sido 3 y no 4. - deeKay

Digamos que x = 1, y = 0, z = 0. No hay forma de obtener x = 0, y = 1, z = 0 de su algoritmo. - Tae-Sung Shin

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.