Generar todas las combinaciones de ciertos dígitos -- python campo con python-2.x campo con combinatorics campo con generator camp codereview Relacionados El problema

Generate all combinations of certain digits


7
vote

problema

Español

relacionado con Otra respuesta de la mía en el Proyecto Euler 35, encontré la necesidad de calcular todas las combinaciones de ciertas Dígitos, es decir, 1, 3, 7 y 9, por debajo de un dado std::list5 . Miré a su alrededor, y no encontré ninguna buena buena buena soluciones PREPADE, pero encontré algunos bits y piezas relacionadas con los productos cartesianos, y recopilaron mis hallazgos en el siguiente código:

  std::list6  

que de hecho imprime la salida esperada de:

  std::list7  

Primero intenté usar std::list8 y otras variaciones de ItroTools, pero algunas de esas versiones no incluían números como 31 y 73. Pero podría simplemente no usar los parámetros correctos.

¿Puedes revisar este código, lo que sugiere alguna uptalización o mejoras?

Original en ingles

Related to another answer of mine on Project Euler 35, I found the need to calculate all combinations of certain digits, i.e. 1, 3, 7 and 9, below a given n. I looked around, and didn't find any really good premade solutions, but found some bits and pieces related to Cartesian products, and collated my findings into the following code:

import itertools  def all_digit_combinations(allowed_digits, maximum):     """Yield all combinations of allowed_digits below maximum.      For allowed_digits being a list of single digits, i.e. [1, 3, 7, 9],     combine all variants of these digits until we pass the maximum value.     """     no_of_digits = 1      while True:         for digit_tuple in itertools.product(allowed_digits, repeat=no_of_digits):             new_number = reduce(lambda rst, d: rst * 10 + d, digit_tuple)             if new_number < maximum:                 yield new_number             else:                 raise StopIteration          no_of_digits += 1   if __name__ == '__main__':     print ', '.join(map(str, all_digit_combinations([1, 3, 7, 9], 100)))     print ', '.join(map(str, all_digit_combinations([3, 5], 1000))) 

Which indeed prints the expected output of:

1, 3, 7, 9, 11, 13, 17, 19, 31, 33, 37, 39, 71, 73, 77, 79, 91, 93, 97, 99 3, 5, 33, 35, 53, 55, 333, 335, 353, 355, 533, 535, 553, 555 

I first tried using itertools.combinations_with_replacement() and other variations from itertools, but some of those versions failed to include numbers like 31 and 73. But it I could very well simply not use the correct parameters.

Can you review this code, suggesting any optimalisations or improvements?

           

Lista de respuestas

8
 
vote
vote
La mejor respuesta
 

Creo que el código que tienes está cerca posible puede convertirse.

Sin embargo, usaría una solución más funcional / basada en iTerator.

  1. //// MAIN /////// let services = [ { url: "x", label: "1" }, { url: "y", label: "2" }, { url: "z", label: "3" }, ]; showServicesToUser(services); //// END MAIN ///// const serviceList = document.querySelector("#serviceList"); function createButtonFromService(service) { let button = document.createElement("BUTTON"); button.innerHTML = service.label; button.addEventListener("click", function () { document.dispatchEvent( new CustomEvent("serviceChosen", { detail: service }) ); }); serviceList.appendChild(button); } function showServicesToUser(services) { if (services.length < 1) return; serviceList.classList.remove("hidden"); for (let service of services) { createButtonFromService(service); } } function chooseService(service) { console.log("service_chosen:", service); // ... go on.... } document.addEventListener("serviceChosen", function (e) { serviceList.classList.add("hidden"); // hide again for we are done chooseService(e.detail); }, { once: true });2 puede ser reemplazado por un bucle para el bucle. Esto es utilizando //// MAIN /////// let services = [ { url: "x", label: "1" }, { url: "y", label: "2" }, { url: "z", label: "3" }, ]; showServicesToUser(services); //// END MAIN ///// const serviceList = document.querySelector("#serviceList"); function createButtonFromService(service) { let button = document.createElement("BUTTON"); button.innerHTML = service.label; button.addEventListener("click", function () { document.dispatchEvent( new CustomEvent("serviceChosen", { detail: service }) ); }); serviceList.appendChild(button); } function showServicesToUser(services) { if (services.length < 1) return; serviceList.classList.remove("hidden"); for (let service of services) { createButtonFromService(service); } } function chooseService(service) { console.log("service_chosen:", service); // ... go on.... } document.addEventListener("serviceChosen", function (e) { serviceList.classList.add("hidden"); // hide again for we are done chooseService(e.detail); }, { once: true });3 . (Generador infinito cuidadoso aquí)

  2. //// MAIN /////// let services = [ { url: "x", label: "1" }, { url: "y", label: "2" }, { url: "z", label: "3" }, ]; showServicesToUser(services); //// END MAIN ///// const serviceList = document.querySelector("#serviceList"); function createButtonFromService(service) { let button = document.createElement("BUTTON"); button.innerHTML = service.label; button.addEventListener("click", function () { document.dispatchEvent( new CustomEvent("serviceChosen", { detail: service }) ); }); serviceList.appendChild(button); } function showServicesToUser(services) { if (services.length < 1) return; serviceList.classList.remove("hidden"); for (let service of services) { createButtonFromService(service); } } function chooseService(service) { console.log("service_chosen:", service); // ... go on.... } document.addEventListener("serviceChosen", function (e) { serviceList.classList.add("hidden"); // hide again for we are done chooseService(e.detail); }, { once: true });4 me implicaría, que los consigo todos, incluso 55555555. En su lugar, haría //// MAIN /////// let services = [ { url: "x", label: "1" }, { url: "y", label: "2" }, { url: "z", label: "3" }, ]; showServicesToUser(services); //// END MAIN ///// const serviceList = document.querySelector("#serviceList"); function createButtonFromService(service) { let button = document.createElement("BUTTON"); button.innerHTML = service.label; button.addEventListener("click", function () { document.dispatchEvent( new CustomEvent("serviceChosen", { detail: service }) ); }); serviceList.appendChild(button); } function showServicesToUser(services) { if (services.length < 1) return; serviceList.classList.remove("hidden"); for (let service of services) { createButtonFromService(service); } } function chooseService(service) { console.log("service_chosen:", service); // ... go on.... } document.addEventListener("serviceChosen", function (e) { serviceList.classList.add("hidden"); // hide again for we are done chooseService(e.detail); }, { once: true });5 un generador infinito.

  3. //// MAIN /////// let services = [ { url: "x", label: "1" }, { url: "y", label: "2" }, { url: "z", label: "3" }, ]; showServicesToUser(services); //// END MAIN ///// const serviceList = document.querySelector("#serviceList"); function createButtonFromService(service) { let button = document.createElement("BUTTON"); button.innerHTML = service.label; button.addEventListener("click", function () { document.dispatchEvent( new CustomEvent("serviceChosen", { detail: service }) ); }); serviceList.appendChild(button); } function showServicesToUser(services) { if (services.length < 1) return; serviceList.classList.remove("hidden"); for (let service of services) { createButtonFromService(service); } } function chooseService(service) { console.log("service_chosen:", service); // ... go on.... } document.addEventListener("serviceChosen", function (e) { serviceList.classList.add("hidden"); // hide again for we are done chooseService(e.detail); }, { once: true });6 es un poco feo. Personalmente, usaría //// MAIN /////// let services = [ { url: "x", label: "1" }, { url: "y", label: "2" }, { url: "z", label: "3" }, ]; showServicesToUser(services); //// END MAIN ///// const serviceList = document.querySelector("#serviceList"); function createButtonFromService(service) { let button = document.createElement("BUTTON"); button.innerHTML = service.label; button.addEventListener("click", function () { document.dispatchEvent( new CustomEvent("serviceChosen", { detail: service }) ); }); serviceList.appendChild(button); } function showServicesToUser(services) { if (services.length < 1) return; serviceList.classList.remove("hidden"); for (let service of services) { createButtonFromService(service); } } function chooseService(service) { console.log("service_chosen:", service); // ... go on.... } document.addEventListener("serviceChosen", function (e) { serviceList.classList.add("hidden"); // hide again for we are done chooseService(e.detail); }, { once: true });7 . (En este caso, es mejor que el filtro, ya que los infinitos están involucrados).

    Como suplente a ItroTools, puede simplemente //// MAIN /////// let services = [ { url: "x", label: "1" }, { url: "y", label: "2" }, { url: "z", label: "3" }, ]; showServicesToUser(services); //// END MAIN ///// const serviceList = document.querySelector("#serviceList"); function createButtonFromService(service) { let button = document.createElement("BUTTON"); button.innerHTML = service.label; button.addEventListener("click", function () { document.dispatchEvent( new CustomEvent("serviceChosen", { detail: service }) ); }); serviceList.appendChild(button); } function showServicesToUser(services) { if (services.length < 1) return; serviceList.classList.remove("hidden"); for (let service of services) { createButtonFromService(service); } } function chooseService(service) { console.log("service_chosen:", service); // ... go on.... } document.addEventListener("serviceChosen", function (e) { serviceList.classList.add("hidden"); // hide again for we are done chooseService(e.detail); }, { once: true });8 . Pero debido a que es implícito, se puede disgustar.

Y así lo volvería a escribir en esto:

  //// MAIN ///////  let services = [     { url: "x", label: "1" },     { url: "y", label: "2" },     { url: "z", label: "3" }, ];  showServicesToUser(services);  //// END MAIN /////  const serviceList = document.querySelector("#serviceList");  function createButtonFromService(service) {     let button = document.createElement("BUTTON");     button.innerHTML = service.label;     button.addEventListener("click", function () {         document.dispatchEvent(             new CustomEvent("serviceChosen", { detail: service })         );     });     serviceList.appendChild(button); }  function showServicesToUser(services) {     if (services.length < 1) return;      serviceList.classList.remove("hidden");      for (let service of services) {         createButtonFromService(service);     } }  function chooseService(service) {     console.log("service_chosen:", service);     // ... go on.... }  document.addEventListener("serviceChosen", function (e) {     serviceList.classList.add("hidden"); // hide again for we are done     chooseService(e.detail); }, { once: true });9  

Como se trata de una actuación, su código es bastante más rápido que el mío. Utilicé el siguiente código:

  .hidden{   visibility: hidden }0  

obtuve los siguientes puntos de referencia.

  .hidden{   visibility: hidden }1  
 

I think the code that you have is near best it can become.

I would however use a more functional/iterator based solution.

  1. while True: no_of_digits += 1 can be replaced with a for loop. This is by using itertools.count(1). (Careful infinite generator here)

  2. all_digit_combinations would imply to me, that I get all of them, even 55555555. Instead I would make all_digit_combinations an infinite generator.

  3. else: raise StopIteration is kinda ugly. Personally I would use itertool.takewhile. (In this case it's better than filter, as infinite's are involved.)

    As an alternate to itertools, you could just return. But due to that being implicit, it may be disliked.

And so I would re-write it into this:

def all_digit_combinations(allowed_digits):     return (         reduce(lambda rst, d: rst * 10 + d, digit_tuple)         for no_of_digits in itertools.count(1)         for digit_tuple in itertools.product(allowed_digits, repeat=no_of_digits)     )  def digit_combinations(allowed_digits, maximum):     return itertools.takewhile(         lambda x: x < maximum,         all_digit_combinations(allowed_digits)     )  if __name__ == '__main__':     print ', '.join(map(str, digit_combinations([1, 3, 7, 9], 100)))     print ', '.join(map(str, digit_combinations([3, 5], 1000))) 

As a performance is concern, your code is quite a bit faster than mine. I used the following code:

import itertools import timeit from functools import wraps  def all_digit_combinations_original(allowed_digits, maximum):     no_of_digits = 1     while True:         for digit_tuple in itertools.product(allowed_digits, repeat=no_of_digits):             new_number = reduce(lambda rst, d: rst * 10 + d, digit_tuple)             if new_number < maximum:                 yield new_number             else:                 raise StopIteration         no_of_digits += 1  def all_digit_combinations_enhanced(allowed_digits, maximum):     for no_of_digits in itertools.count(1):         for digit_tuple in itertools.product(allowed_digits, repeat=no_of_digits):             new_number = reduce(lambda rst, d: rst * 10 + d, digit_tuple)             if new_number < maximum:                 yield new_number             else:                 raise StopIteration  def all_digit_combinations_answer(allowed_digits):     return (         reduce(lambda rst, d: rst * 10 + d, digit_tuple)         for no_of_digits in itertools.count(1)         for digit_tuple in itertools.product(allowed_digits, repeat=no_of_digits)     )  def digit_combinations_answer(allowed_digits, maximum):     return itertools.takewhile(         lambda x: x < maximum,         all_digit_combinations_answer(allowed_digits)     )  def digit_combinations_enhanced(allowed_digits, maximum):     return itertools.takewhile(         lambda x: x < maximum,         (             reduce(lambda rst, d: rst * 10 + d, digit_tuple)             for no_of_digits in itertools.count(1)             for digit_tuple in itertools.product(allowed_digits, repeat=no_of_digits)         )     )  def timer_wrapper(*args, **kwargs):     def wrapper(fn):         @wraps(fn)         def run():             list(fn(*args, **kwargs))         return run     return wrapper  def timeit_(fn):     print fn.__name__, timeit.timeit(fn)  if __name__ == '__main__':     timer = timer_wrapper([1, 3, 7, 9], 100)     timeit_(timer(all_digit_combinations_original))     timeit_(timer(digit_combinations_answer))     timeit_(timer(all_digit_combinations_enhanced))     timeit_(timer(digit_combinations_enhanced))      timer = timer_wrapper([3, 5], 1000)     timeit_(timer(all_digit_combinations_original))     timeit_(timer(digit_combinations_answer))     timeit_(timer(all_digit_combinations_enhanced))     timeit_(timer(digit_combinations_enhanced)) 

I obtained the following benchmarks.

all_digit_combinations_original 14.666793108 digit_combinations_answer 18.1881940365 all_digit_combinations_enhanced 15.228415966 digit_combinations_enhanced 17.9368011951  all_digit_combinations_original 14.4108960629 digit_combinations_answer 17.2161641121 all_digit_combinations_enhanced 14.8418960571 digit_combinations_enhanced 17.1169350147 
 
 
 
 
5
 
vote

Puede mejorar el rendimiento cambiando el reduce en un simple sum Si realiza el 9988777665544332 de las listas de las, Tens, cientos, etc. .

  import itertools  def digit_combinations_jk(allowed_digits, maximum):     choices = [allowed_digits]     while True:         for terms in itertools.product(*choices):             new_number = sum(terms)             if new_number < maximum:                 yield new_number             else:                 return         choices.insert(0, [10 * x for x in choices[0]])   

Punto de referencia de Joe Wallis:

  all_digit_combinations_original 12.2259960175 digit_combinations_answer 15.1491339207 all_digit_combinations_enhanced 12.3741300106 digit_combinations_enhanced 14.9268059731 digit_combinations_jk 8.92141604424  all_digit_combinations_original 11.867401123 digit_combinations_answer 14.2185270786 all_digit_combinations_enhanced 11.9946269989 digit_combinations_enhanced 14.0026550293 digit_combinations_jk 8.62084293365   
 

You can improve performance by changing the reduce into a simple sum if you take the product from lists of ones, tens, hundreds etc.

import itertools  def digit_combinations_jk(allowed_digits, maximum):     choices = [allowed_digits]     while True:         for terms in itertools.product(*choices):             new_number = sum(terms)             if new_number < maximum:                 yield new_number             else:                 return         choices.insert(0, [10 * x for x in choices[0]]) 

Joe Wallis's benchmark:

all_digit_combinations_original 12.2259960175 digit_combinations_answer 15.1491339207 all_digit_combinations_enhanced 12.3741300106 digit_combinations_enhanced 14.9268059731 digit_combinations_jk 8.92141604424  all_digit_combinations_original 11.867401123 digit_combinations_answer 14.2185270786 all_digit_combinations_enhanced 11.9946269989 digit_combinations_enhanced 14.0026550293 digit_combinations_jk 8.62084293365 
 
 

Relacionados problema

4  Creación de un índice invertido de documentos de texto  ( Creating an inverted index from text documents ) 
Estoy trabajando en un proyecto de recuperación de información, donde tengo que procesar un Datos de texto de ~ 1.5 GB y crear un diccionario (palabras, frecu...

3  Parser de texto implementado como generador  ( Text parser implemented as a generator ) 
A menudo necesito analizar el texto separado por pestañas (generalmente de un archivo enorme) a los registros. Escribí un generador para hacer eso por mí; ¿Ha...

1  Función del generador para enumerar archivos  ( Generator function to enumerate files ) 
Quiero implementar una función que enumera todos los archivos en el directorio. Creo que la función debe devuelve una promesa , porque se prefiere la llam...

9  Generador de cadena aleatorio en C  ( Random string generator in c ) 
He creado esta pequeña función solo para practicar el código C. Es un simple generador de cadenas aleatorias. #include <string.h> #include <time.h> char *...

4  Combinando fragmentos de datos en una tubería del generador  ( Combining fragments of data in a generator pipeline ) 
Editar: Parece que el enfoque de programación basado en flujo (o reactivo) ayudaría aquí; Hay algunas bibliotecas en Python que lo intentan. Intenté seguir ...

2  ASYNC LINKED LISTA DE Y DESYNC ITERABLE  ( Async linked list to and from async iterable ) 
Estoy trabajando en algunas cosas que funciona mejor con listas vinculadas (inmutables ASYNC), como esta: { value: 'one', next: async () => { value: 'two',...

3  Aplanamiento recursivo de secuencias rápidas - un enfoque demasiado complicado  ( Recursive flattening of swift sequences an overly complicated approach ) 
Recientemente leí y respondí Martin R 's Recursive Aplanking of Swift Secuencias y continuó jugando con el código hasta que llegué a algo que era bastante...

2  Proyecto EULER # 2: Suma de incluso números de Fibonacci, utilizando generadores  ( Project euler 2 sum of even fibonacci numbers using generators ) 
Actualmente estoy trabajando en Project Euler Problem # 2, lo que requiere que imprima la suma de todos los números de Fibonacci todos incluso por debajo ...

10  Uso de generadores para imprimir la estructura similar a un árbol de un proyecto  ( Using generators to print the tree like structure of a project ) 
El objetivo de este código es imprimir el árbol de entidad de un proyecto VHDL. Hay un readme y pruebas muy mínimas en la github repo . Estoy tratando de h...

1  Una clase con un puntero de función en lugar de un generador  ( A class with a function pointer instead of a generator ) 
Estoy construyendo archivos Tikz, un PDF para cada imagen que tengo. El propósito es agregar texto a cada uno por separado. Las imágenes son LEGION, así que c...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos