Calcolo efficiente dell'intero più piccolo con n divisori


9

Per affrontare questo problema, l'ho osservato per la prima volta

ϕ(p1e1 p2e2 pkek)=(e1+1)(e2+1)(ek+1)

Dove ϕ(m) è il numero di divisori (non necessariamente primi) di m . Se m è il numero intero più piccolo in modo che ϕ(m)=n , allora

ϕ(m)=n
(e1+1)(e2+1)(ek+1)=n

Ora dobbiamo scegliere ei tale che ipiei sia minimo. Le scelte per p sono banali: sono solo i numeri primi in ordine crescente.

Tuttavia, il mio primo pensiero per la scelta ei non era corretto. Ho pensato che potresti semplicemente fattorizzare n , ordinare i fattori in ordine decrescente e sottrarre 1. Il più delle volte questo funziona bene, ad es. Il numero intero più piccolo con n=15 divisori è:

15 = ( 4 + 1 ) ( 2 + 1 ) m = 2 4 3 2 = 144

15=53
15=(4+1)(2+1)
m=2432=144

Ma questo non è corretto per :n=16

16 = ( 1 + 1 ) ( 1 + 1 ) ( 1 + 1 ) ( 1 + 1 ) m = 2 1 3 1 5 1 7 1 = 210

16=2222
16=(1+1)(1+1)(1+1)(1+1)
m=21315171=210

Considerando che la risposta corretta è:

m = 2 3 3 1 5 1 = 120

16=(3+1)(1+1)(1+1)
m=233151=120

Quindi è chiaro a volte dobbiamo unire i fattori. In questo caso perché . Ma non vedo esattamente una strategia di fusione pulita e diretta. Ad esempio, si potrebbe pensare che dobbiamo sempre unirci al potere 2 , ma questo non è vero:71>222

m = 2 96 3 1 5 1 7 1 11 1 > 2 96 3 3 5 1 7 1

1552=(96+1)(1+1)(1+1)(1+1)(1+1)
m=296315171111>296335171

Non riesco a pensare immediatamente a un esempio, ma il mio istinto dice che alcuni approcci avidi possono fallire se prima uniscono i poteri sbagliati.

Esiste una semplice strategia ottimale per unire questi poteri per ottenere la risposta corretta?


n=3072

22315171111131171191231291311

23325171111131171191231291

25335171111131171191231

Tuttavia la soluzione ottimale è:

27335271111131171191

n24m2k1log(2)+k2log(3)k1k2=24mm

Risposte:


1

Ecco una soluzione, basata sui miei commenti sopra. Non sostengo che sia ottimale.

T(n,m)nm

T(n,1)=2n1T(2m,m)=p1p2pm

E abbiamo anche la ricorrenza:

T(n,m)=mind|n[T(nd,m1)pmd1]

Infine, la quantità che stai cercando è

min1ilog(n)T(n,i)

A tal fine, ecco un po 'di codice Python, che concorda con tutti i numeri che hai dato sopra. Nota che funziona con i logaritmi per mantenere i numeri più piccoli: quindi l'intero numero effettivo che cerchi è round(2**smallest(n)).

import functools
import itertools
import math

# All primes less than 100.
PRIMES = [
  2, 3, 5, 7, 11,
  13, 17, 19, 23, 29,
  31, 37, 41, 43, 47,
  53, 59, 61, 67, 71,
  73, 79, 83, 89, 97,
]

LOG_PRIMES = [math.log2(p) for p in PRIMES]

def smallest(n):
  max_factors = math.ceil(math.log2(n))
  min_so_far = float('Infinity')
  factors = factorize(n)
  memo = {}
  for i in range(1, max_factors+1):
    t = T(n,i, factors, memo)
    if 0.0 < t < min_so_far:
      min_so_far = t
  return min_so_far

def T(n, m, factors=None, memo=None):
  if memo is None:
    memo = {}
  if n < 2 or m < 1:
    return 0
  elif m == 1:
    # Everything on the smallest prime.
    return (n-1) * LOG_PRIMES[0]
  elif n < 2**m:
    return 0
  elif n == 2**m:
    # Product of first m primes, in log.
    return sum(LOG_PRIMES[:m])
  elif (n,m) in memo:
    return memo[(n,m)]

  if factors is None:
    factors = factorize(n)
  if len(factors) < m:
    return 0

  smallest = float('Infinity')  
  for factor_list in powerset(factors):
    divisor = product(factor_list)
    first = T(divisor, m-1, factor_list, memo)
    # No such product.
    if first < 1.0:
      continue
    second = (n/divisor - 1) * LOG_PRIMES[m-1]
    total = first + second
    if total < smallest:
      smallest = total

  memo[(n,m)] = smallest
  return smallest

def product(nums):
  return functools.reduce(lambda x,y: x*y, nums, 1)

def factorize(n):
  prime_factors = []
  for p in PRIMES:
    while n%p == 0:
      n //= p
      prime_factors.append(p)
    if n == 1:
      break
  return prime_factors

def powerset(lst):
  # No empty set.
  return itertools.chain.from_iterable(itertools.combinations(lst, r) 
                                       for r in range(1, len(lst)+1))

I commenti a cui ti riferisci sembrano essere stati purtroppo cancellati, ma questo è certamente ottimale (nel senso di calcolare il numero intero più piccolo possibile con esattamente fattori). È l'ottimalità della complessità temporale di cui non sei sicuro? Non conosco un limite stretto per il numero di divisori di un numero intero , ma anche con il limite molto pessimistico di tuo algoritmo è solo , che dovrebbe essere abbastanza veloce per in decine di migliaia! (A proposito: stavo scrivendo lo stesso algoritmo (meno alcune ottimizzazioni) ma ci sei arrivato prima, ben fatto!)nnO(n)O(n2logn)n
j_random_hacker

@j_random_hacker: Sì, non sono sicuro di cosa sia successo a quei commenti: ce n'erano molti, e ora sono spariti tutti! Stavo davvero parlando della complessità temporale; In realtà penso che sia probabilmente più vicino a , ma il numero di divisori è una funzione complicata. Naturalmente, il codice sopra può sicuramente essere ottimizzato meglio: non tiene conto dei duplicati, ad esempio. O(nlogn)powerset
Steve D,

Credo che sia più facile da implementare in modo efficiente utilizzando la programmazione dinamica: gist.github.com/orlp/0fbb7784782712bc7c411aa58a188143 Non mi sento davvero a mio agio con il trucco del logaritmo a proposito: la precisione limitata in virgola mobile a un certo punto rovinerà le cose. Detto questo, non credo che questo sia effettivamente più veloce della generazione di tutte le partizioni moltiplicative. In effetti, credo che sia esattamente quello che sta facendo sotto mentite spoglie!
orlp

Dopo aver letto il commento di @ orlp e il tuo codice più da vicino, ora penso che sia importante che la complessità temporale (e le prestazioni pratiche) cambino for factor_list in powerset(factors)in qualcosa che genera ogni divisore distinto nesattamente una volta. In questo modo, per esempio, , quando consideri le soluzioni che contengono esattamente i primi primi come fattori primi distinti, eseguirai solo lavori non ricorsivi invece di , che è esponenziale in . 2 k O ( k 2 ) O ( ( 2 kn=2k3k2kO(k2)O((2kk))k
j_random_hacker il

1
@orlp: ho frainteso il termine "partizioni moltiplicative", scusa. Grazie per il codice Python. Per capire perché l'algoritmo di Steve D non è parallelo a quel codice, considera multiplicative_partitions(24), che produce (tra gli altri) le partizioni [4, 3, 2]e [6, 2, 2]che (dopo aver invertito l'ordine per dare al fattore primo più piccolo l'esponente più alto) corrispondono alle soluzioni e , rispettivamente. L'algoritmo di Steve D non prenderà mai in considerazione quest'ultima soluzione, poiché ha già determinato che la sottosoluzione . 2332512531512332=72<2531=96
j_random_hacker,

-1

I possibili candidati per "numero intero più piccolo con n divisori" sono i numeri interi della forma dove a ≥ b ≥ c ... e (a + 1) (b + 1) (c + 1) ... = n.2a·3b·5c...

Quindi devi trovare tutti i modi per esprimere n come prodotto di numeri interi ≥ 2 in ordine non crescente, e calcolare e controllare i candidati corrispondenti. Ad esempio, quando n = 16, 16 = 8 · 2 = 4 · 4 = 4 · 2 · 2 = 2 · 2 · 2 · 2, le possibilità sono , , , e il più piccolo è .27·323·3323·3·52·3·5·723·3·5=120

Se n è il prodotto di due numeri primi p · q, p ≥ q, gli unici candidati sono e e quest'ultimo è sempre più piccolo .2pq12p1·3q1

Puoi capire alcune condizioni quando potrebbe esserci un fattore per esempio, controllando se per alcuni prime x non è un fattore. Nell'esempio n = 16, esiste un fattore perché .2ab12ab1>2a1·xb12323<2·7


3
Perdonami, ma questo non risponde affatto alla mia domanda, riassume semplicemente ciò che ho trovato nella mia domanda. Il titolo è proprio questo: un titolo, non la domanda stessa. Sento che hai letto solo il titolo prima di rispondere. La vera domanda è in fondo al testo della mia domanda.
orlp

Questa è la risposta nell'ultimo paragrafo.
gnasher729,

@ gnasher729 Questo è ben lungi dall'essere una risposta a una domanda "calcolare in modo efficiente", o anche per "strategia ottimale per la fusione"
yo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.