Effiziente Berechnung der kleinsten Ganzzahl mit n Teilern


9

Um dieses Problem anzugehen, habe ich das zuerst beobachtet

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

Wobei die Anzahl der (nicht unbedingt primären) Teiler von . Wenn die kleinste ganze Zahl ist, so dass , dannϕ(m)mmϕ(m)=n

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

Jetzt müssen wir so wählen , dass minimal ist. Die Auswahlmöglichkeiten für p sind trivial - sie sind nur die Primzahlen in aufsteigender Reihenfolge.eiipieip

Mein erster Gedanke für die Wahl von ei war jedoch falsch. Ich dachte, Sie könnten einfach n faktorisieren, die Faktoren in absteigender Reihenfolge sortieren und 1 subtrahieren. Meistens funktioniert dies einwandfrei, z. B. ist die kleinste ganze Zahl mit n=15 Teilern:

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

Dies ist jedoch falsch für 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

Die richtige Antwort lautet:

m = 2 3 3 1 5 1 = 120

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

Es ist also klar, dass wir manchmal Faktoren zusammenführen müssen. In diesem Fall weil . Aber ich sehe nicht gerade eine saubere und direkte Zusammenführungsstrategie. Zum Beispiel könnte man denken, wir müssen immer in die Potenzen verschmelzen , aber das ist nicht wahr: 271>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

Ich kann mir nicht sofort ein Beispiel vorstellen, aber mein Instinkt sagt, dass einige gierige Ansätze scheitern können, wenn sie zuerst die falschen Kräfte zusammenführen.

Gibt es eine einfache optimale Strategie, um diese Kräfte zusammenzuführen, um die richtige Antwort zu erhalten?


Nachtrag. Ein gieriger Algorithmus, der jede mögliche Zusammenführung überprüft und die beste auf Zusammenführungsbasis ausführt, schlägt bei fehl . Die Reihe der Einzelzusammenführungen lautet:n=3072

22315171111131171191231291311

23325171111131171191231291

25335171111131171191231

Die optimale Lösung ist jedoch:

27335271111131171191

@orlp: Mein Vorschlag war: fix (sagen wir ) und fix (sagen wir ). Dann versuchen Sie, zu minimieren , vorbehaltlich . Wenn Sie also mit einer festen Anzahl (von Primzahlen) arbeiten, können Sie die Komplikationen ignorieren, ob eine bestimmte Primzahl im globalen Minimum erscheinen soll oder nicht. Sie finden das Minimum für jedes und nehmen dann das min von diesen. n24m2k1Log(2)+k2Log(3)k1k2=24mm
Steve D

Antworten:


1

Hier ist eine Lösung, basierend auf meinen obigen Kommentaren. Ich mache keine Ansprüche, das ist optimal.

Die Idee ist, , das wir als "kleinste positive ganze Zahl mit genau Teilern und verschiedenen Primfaktoren" definieren. Wir machen die einfachen Beobachtungen:T.(n,m)nm

T.(n,1)=2n- -1T.(2m,m)=p1p2pm

Und wir haben auch die Wiederholung:

T.(n,m)=Mindestd|n[T.(nd,m- -1)pmd- -1]]

Schließlich ist die Menge

Mindest1ichLog(n)T.(n,ich)

Zu diesem Zweck finden Sie hier einen Python-Code, der mit allen oben angegebenen Zahlen übereinstimmt. Beachten Sie, dass es mit den Logarithmen funktioniert, um die Zahlen kleiner zu halten: Die tatsächlich gesuchte Ganzzahl ist also 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))

Die Kommentare, auf die Sie sich beziehen, scheinen leider gelöscht worden zu sein, aber dies ist sicherlich optimal (im Sinne der Berechnung der kleinstmöglichen Ganzzahl mit genau Faktoren). Ist es die Optimalität der Zeitkomplexität, bei der Sie sich nicht sicher sind? Ich kenne keine enge Grenze für die Anzahl der Teiler einer ganzen Zahl , aber selbst mit der sehr pessimistischen Grenze von Ihr Algorithmus nur , was schnell genug sein sollte zu Zehntausenden! (Übrigens: Ich habe den gleichen Algorithmus geschrieben (abzüglich einiger Optimierungen), aber Sie sind zuerst dort nnÖ(n)Ö(n2Logn)n
angekommen

@j_random_hacker: Ja, ich bin mir nicht sicher, was mit diesen Kommentaren passiert ist: Es gab viele von ihnen und sie sind jetzt alle weg! Ich habe in der Tat über zeitliche Komplexität gesprochen; Ich denke tatsächlich, dass es wahrscheinlich näher an , aber die Anzahl der Teiler ist eine schwierige Funktion. Natürlich kann der obige Code sicherlich besser optimiert werden: Berücksichtigt beispielsweise keine Duplikate. Ö(nLogn)powerset
Steve D

Ich glaube, dass dies mit dynamischer Programmierung einfacher effizient zu implementieren ist: gist.github.com/orlp/0fbb7784782712bc7c411aa58a188143 Ich bin übrigens mit dem Logarithmus-Trick wirklich nicht vertraut - Gleitkomma-begrenzte Präzision wird irgendwann die Dinge durcheinander bringen . Abgesehen davon glaube ich nicht, dass dies tatsächlich schneller ist als das Generieren aller multiplikativen Partitionen. Tatsächlich glaube ich, dass es genau das ist, was es in Verkleidung tut!
Orlp

Nachdem ich den Kommentar von @ orlp und Ihren Code genauer gelesen habe, denke ich jetzt, dass es wichtig ist, dass sich die zeitliche Komplexität (und die praktische Leistung) for factor_list in powerset(factors)in etwas ändert , das jeden einzelnen Teiler ngenau einmal erzeugt. Auf diese Weise können Sie beispielsweise für , wenn Sie Lösungen betrachten, die genau die ersten Primzahlen als unterschiedliche Primfaktoren enthalten, nur nicht rekursiv anstelle von , was in exponentiell ist . n=2k3k2kÖ(k2)Ö((2kk))k
j_random_hacker

1
@orlp: Ich habe den Begriff "multiplikative Partitionen" falsch verstanden, sorry. Danke für den Python-Code. Um zu sehen, warum der Algorithmus von Steve D diesem Code nicht entspricht, betrachten Sie multiplicative_partitions(24), welche (unter anderem) die Partitionen erzeugt [4, 3, 2]und [6, 2, 2]welche (nach Umkehrung der Reihenfolge, um dem kleinsten Primfaktor den höchsten Exponenten zu geben) den Lösungen bzw. . Der Algorithmus von Steve D wird die letztere Lösung niemals berücksichtigen, da er bereits festgestellt hat, dass die Unterlösung . 2332512531512332=72<2531=96
j_random_hacker

-1

Mögliche Kandidaten für "kleinste ganze Zahl mit n Teilern" sind die ganzen Zahlen der Form wobei a ≥ b ≥ c ... und (a + 1) (b + 1) (c + 1) ... = n.2ein· ·3b· ·5c...

Sie müssen also alle Möglichkeiten finden, um n als Produkt von ganzen Zahlen ≥ 2 in nicht aufsteigender Reihenfolge auszudrücken und die entsprechenden Kandidaten zu berechnen und zu überprüfen. Wenn zum Beispiel n = 16, 16 = 8 · 2 = 4 · 4 = 4 · 2 · 2 = 2 · 2 · 2 · 2 ist, sind die Möglichkeiten , , , , und das kleinste ist .27· ·323· ·3323· ·3· ·52· ·3· ·5· ·723· ·3· ·5=120

Wenn n das Produkt zweier Primzahlen p · q, p ≥ q ist, sind die einzigen Kandidaten und , und letzterer ist immer kleiner .2pq- -12p- -1· ·3q- -1

Sie können eine Bedingung herausfinden, wenn es einen Faktor , indem Sie beispielsweise prüfen, ob für einige prime x das ist kein faktor. Im Beispiel n = 16 gibt es einen Faktor weil .2einb- -12einb- -1>2ein- -1· ·xb- -12323<2· ·7


3
Vergib mir, aber das beantwortet meine Frage überhaupt nicht, es fasst nur zusammen, was ich in meiner Frage gefunden habe. Der Titel ist genau das: ein Titel, nicht die Frage selbst. Ich habe das Gefühl, Sie haben den Titel erst gelesen, bevor Sie geantwortet haben. Die eigentliche Frage befindet sich am Ende meines Fragetextes.
Orlp

Das wird im letzten Absatz beantwortet.
Gnasher729

@ gnasher729 Das ist alles andere als eine Antwort auf eine Frage "effizient rechnen" oder sogar für "optimale Strategie für das Zusammenführen"
yo
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.