En küçük tamsayıyı n bölenlerle verimli bir şekilde hesaplama


9

Bu sorunu çözmek için önce şunu gözlemledim:

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

Burada arasında (zorunlu olarak asal olan) bölenler sayısıdır m . Eğer m küçük tamsayıdır öyle ki \ phi (m) = n sonra,ϕ(m)mmϕ(m)=n

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

Şimdi seçmelisiniz ei böyle ipiei minimumdur. p için seçenekler önemsiz - onlar sadece artan düzende ilkeler.

Ancak, e_i'yi seçmeye yönelik ilk düşüncem eiyanlıştı. Basitçe n faktör, azalan sırayla faktörleri sıralamak ve 1 çıkarmak düşündüm . Çoğu zaman bu iyi çalışır, örneğin n=15 bölenleri ile en küçük tamsayı :

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

Ancak bu n = 16 için yanlıştır n=16:

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

Oysa doğru cevap:

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

Bu yüzden bazen faktörleri birleştirmemiz gerektiği açıktır. Bu durumda . Ancak tam olarak temiz ve doğrudan birleşme stratejisi görmüyorum. Örneğin, kişi her zaman güçle birleşmemiz gerektiğini düşünebilir , ancak bu doğru değildir:71>222

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

Hemen bir örnek düşünemiyorum ama içgüdüm, ilk önce yanlış güçleri birleştirmeleri durumunda bazı açgözlü yaklaşımların başarısız olabileceğini söylüyor.

Doğru cevabı almak için bu güçleri birleştirmek için basit bir optimal strateji var mı?


Ek. Mümkün olan her birleştirmeyi denetleyen ve en iyi olanı bir birleştirme temelinde gerçekleştiren açgözlü bir algoritma başarısız olur . Tek tek birleştirme serisi:n=3072

22315171111131171191231291311

23325171111131171191231291

25335171111131171191231

Ancak en uygun çözüm:

27335271111131171191

@orlp: Benim önerim şuydu: fix (diyelim, ) ve fix (diyelim, ). Sonra tabi olarak değerini . Bu nedenle, sabit bir sayısı (asal) ile çalışarak, belirli bir asalın küresel minimumda görünüp görünmemesinin komplikasyonlarını göz ardı edebilirsiniz. Her için minimumu bulursunuz , daha sonra min. n24m2k1log(2)+k2log(3)k1k2=24mm
Steve D

Yanıtlar:


1

İşte yukarıdaki yorumlarıma dayalı bir çözüm. Bunun en uygun olduğunu iddia etmiyorum.

Fikir kabul etmektir biz "tam olan en küçük pozitif tamsayı olarak tanımlar, bölenler ve farklı asal çarpanları". Kolay gözlemler yapıyoruz:T(n,m)nm

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

Ve ayrıca nüks var:

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

Son olarak, aradığınız miktar

min1ilog(n)T(n,i)

Bu amaçla, yukarıda verdiğiniz tüm sayıları kabul eden bazı Python kodları. Sayıları daha küçük tutmak için logaritmalarla birlikte çalıştığını unutmayın: böylece aradığınız gerçek tamsayı 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))

Bahsettiğiniz yorumlar maalesef silinmiş gibi görünüyor, ancak bu kesinlikle en uygunudur (mümkün olan en küçük tamsayıyı tam olarak faktörle hesaplamak anlamında ). Emin olmadığınız zaman karmaşıklığının en iyiliği mi? Bir tamsayı bölücülerinin sayısı için sıkı bir bağ bilmiyorum , ama nin çok kötümser sınırı ile bile algoritmanız sadece , ki bu da yeterince hızlı olmalı on binlerce! (BTW: Aynı algoritmayı yazıyordum (eksi bazı optimizasyonlar) ama önce oraya geldiniz, aferin!)nnO(n)O(n2logn)n
j_random_hacker

@j_random_hacker: Evet, bu yorumlara ne olduğundan emin değilim: birçoğu vardı ve şimdi hepsi gitti! Gerçekten zaman karmaşıklığından bahsediyordum; Aslında muhtemelen daha yakın olduğunu düşünüyorum , ancak bölen sayısı zor bir işlevdir. Tabii ki, yukarıdaki kod kesinlikle daha iyi optimize edilebilir: örneğin, kopyaları hesaba katmaz. O(nlogn)powerset
Steve D

Bunun dinamik programlama kullanarak verimli bir şekilde uygulanmasının daha kolay olduğuna inanıyorum: gist.github.com/orlp/0fbb7784782712bc7c411aa58a188143 Bu arada logaritma hilesi ile gerçekten rahat değilim - kayan nokta sınırlı hassasiyet bir noktada vidalı şeyler olacak. Bununla birlikte, bunun aslında tüm çarpımsal bölümleri oluşturmaktan daha hızlı olduğuna inanmıyorum. Aslında, tam da kılık değiştirerek yaptığı şeyin bu olduğuna inanıyorum!
orlp

@ Orlp'nin yorumunu ve kodunuzu daha yakından okuduktan sonra, zaman karmaşıklığının (ve pratik performansın) for factor_list in powerset(factors)her bir ayrı böleni ntam olarak bir kez üreten bir şeye geçmesinin önemli olduğunu düşünüyorum . Bu şekilde, örneğin, , tam olarak ilk primeri içeren çözümleri ayrı ana faktörleri olarak düşündüğünüzde , yerine yalnızca özyinelemesiz çalışma yaparsınız. , k'de . n=2k3k2kO(k2)O((2kk))k
j_random_hacker

1
@orlp: "Çoklayıcı bölümler" terimini yanlış anladım, üzgünüm. Python kodu için teşekkürler. Steve D'nin algoritmasının neden bu koda paralel olmadığını görmek için multiplicative_partitions(24), (diğerlerinin yanı sıra) bölümleri üreten [4, 3, 2]ve [6, 2, 2](en küçük ana faktöre en yüksek üssü vermek için düzeni tersine çevirdikten sonra) çözümlere karşılık gelen düşünün. ve . Zaten o subsolution belirlemiştir beri Steve D's algoritması, ikincisi çözümü kabul asla . 2332512531512332=72<2531=96
j_random_hacker

-1

"N bölen en küçük tamsayı" için olası adaylar formunun tam sayılarıdır; burada ≥ b ≥ c ... ve (a + 1) (b + 1) (c + l) ... = n.2a·3b·5c...

Bu nedenle, n değerini artan sayılarda ≥ 2 tamsayısının ürünü olarak ifade etmenin tüm yollarını bulmanız ve karşılık gelen adayları hesaplamanız ve kontrol etmeniz gerekir. Örneğin, n = 16, 16 = 8 · 2 = 4 · 4 = 4 · 2 · 2 = 2 · 2 · 2 · 2 olduğu için olasılıklar , , , ve en küçüğü .27·323·3323·3·52·3·5·723·3·5=120

Eğer n iki p · q, p ≥ q primerinin ürünü ise, tek adaylar ve ve ikincisi her zaman daha küçüktür .2pq12p1·3q1

Olabilir zaman bazı koşul anlamaya bir faktör , örneğin kontrol ederek olmadığını için bazı asal x bu bir faktör değildir. Örneğin, n = 16, bir faktör olduğu için .2ab12ab1>2a1·xb12323<2·7


3
Affet beni, ama bu sorumu hiç cevaplamıyor, sadece sorumda bulduğum şeyi özetliyor. Başlık sadece şudur: Sorunun kendisi değil , bir başlık . Cevap vermeden önce sadece başlığı okuduğunuzu hissediyorum. Asıl soru soru metnimin altında.
orlp

Bu son paragrafta cevaplanır.
gnasher729

@ gnasher729 Bu, "verimli bir şekilde hesaplama" veya hatta "birleştirme için en uygun strateji" sorusunun cevabı olmaktan çok uzaktır
yo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.