Komputasi bilangan bulat terkecil secara efisien dengan n pembagi

9

Untuk mengatasi masalah ini saya pertama kali mengamati itu

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

Di mana adalah jumlah pembagi (tidak harus prima) dari . Jika adalah bilangan bulat terkecil sehingga , makaϕ(m)mmϕ(m)=n

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

Sekarang kita harus memilih sedemikian rupa sehingga minimal. Pilihan untuk sepele - mereka hanya bilangan prima dalam urutan menaik.eiipieip

Namun, pemikiran pertama saya untuk memilih salah. Saya pikir Anda bisa memfaktorkan , mengurutkan faktor dalam urutan menurun dan mengurangi 1. Sebagian besar waktu ini berfungsi dengan baik, misalnya bilangan bulat terkecil dengan pembagi adalah:einn=15

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

Tetapi ini tidak benar untuk :n=16

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

Sedangkan jawaban yang benar adalah:

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

Jadi jelas kadang-kadang kita perlu menggabungkan faktor. Dalam hal ini karena . Tapi saya tidak benar-benar melihat strategi penggabungan yang bersih dan langsung. Sebagai contoh, orang mungkin berpikir kita harus selalu bergabung dengan kekuatan, tetapi ini tidak benar: 271>222

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

Saya tidak dapat langsung memikirkan contoh, tetapi insting saya mengatakan bahwa beberapa pendekatan rakus dapat gagal jika mereka menggabungkan kekuatan yang salah terlebih dahulu.

Apakah ada strategi optimal sederhana untuk menggabungkan kekuatan-kekuatan ini untuk mendapatkan jawaban yang benar?


Tambahan. Algoritma serakah yang memeriksa setiap kemungkinan penggabungan dan melakukan yang terbaik berdasarkan penggabungan-penggabungan, gagal pada . Rangkaian penggabungan satu-per-satu adalah:n=3072

22315171111131171191231291311

23325171111131171191231291

25335171111131171191231

Namun solusi optimalnya adalah:

27335271111131171191
orlp
sumber
@ orlp: Saran saya adalah: fix (katakanlah, ), dan fix (katakanlah, ). Kemudian Anda mencoba meminimalkan , tunduk pada . Jadi dengan bekerja dengan angka tetap (dari bilangan prima), Anda dapat mengabaikan komplikasi apakah prime tertentu harus muncul dalam minimum global atau tidak. Anda menemukan minimum untuk setiap , lalu ambil min dari itu. 24 m 2 k 1 log ( 2 ) + k 2 log ( 3 ) k 1 k 2 = 24 m mn24m2k1log(2)+k2log(3)k1k2=24mm
Steve D

Jawaban:

1

Inilah solusinya, berdasarkan komentar saya di atas. Saya tidak mengklaim ini optimal.

Idenya adalah untuk mempertimbangkan , yang kami definisikan sebagai "bilangan bulat positif terkecil dengan tepat pembagi dan faktor prima yang berbeda". Kami melakukan pengamatan mudah:n mT(n,m)nm

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

Dan kami juga memiliki pengulangan:

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

Akhirnya, jumlah yang Anda cari adalah

min1ilog(n)T(n,i)

Untuk itu, berikut adalah beberapa kode Python, yang setuju dengan semua angka yang Anda berikan di atas. Perhatikan bahwa ia bekerja dengan logaritma untuk menjaga angka lebih kecil: sehingga bilangan bulat yang sebenarnya Anda cari adalah 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))
Steve D
sumber
Komentar yang Anda lihat tampaknya telah dihapus sayangnya, tetapi ini tentu saja optimal (dalam arti menghitung integer terkecil yang mungkin dengan tepat faktor). Apakah optimalitas dari kompleksitas waktu yang Anda tidak yakin? Saya tidak tahu batas ketat untuk jumlah pembagi dari bilangan bulat , tetapi bahkan dengan batas sangat pesimis algoritma Anda hanya , yang harus cukup cepat untuk dalam puluhan ribu! (BTW: Saya sedang menulis algoritma yang sama (minus beberapa optimisasi) tetapi Anda sampai di sana lebih dulu, bagus!)n O ( n ) O ( n 2 log n ) nnnHAI(n)HAI(n2catatann)n
j_random_hacker
@j_random_hacker: Ya, tidak yakin apa yang terjadi pada komentar-komentar itu: ada banyak dari mereka, dan sekarang semuanya hilang! Saya memang berbicara tentang kompleksitas waktu; Saya benar-benar berpikir itu mungkin lebih dekat ke , tetapi jumlah pembagi adalah fungsi yang rumit. Tentu saja, kode di atas tentu saja dapat dioptimalkan dengan lebih baik: misalnya , tidak memperhitungkan duplikat. O(nlogn)powerset
Steve D
Saya percaya ini lebih mudah untuk diterapkan secara efisien menggunakan pemrograman dinamis: gist.github.com/orlp/0fbb7784782712bc7c411aa58a188143 Saya benar-benar tidak nyaman dengan trik logaritma omong-omong - presisi terbatas floating point akan pada beberapa titik hal sekrup. Yang sedang berkata, saya tidak percaya ini sebenarnya lebih cepat daripada menghasilkan semua partisi multiplikasi. Bahkan, saya yakin itulah yang dilakukannya dengan menyamar!
orlp
Setelah membaca komentar @ orlp dan kode Anda lebih dekat, sekarang saya pikir penting untuk kompleksitas waktu (dan kinerja praktis) untuk mengubah for factor_list in powerset(factors)ke sesuatu yang menghasilkan masing-masing pembagi yang berbeda ntepat sekali. Dengan begitu, untuk, katakanlah, , ketika Anda mempertimbangkan solusi yang mengandung persis bilangan prima pertama sebagai faktor utama yang berbeda, Anda hanya akan melakukan pekerjaan non-rekursif daripada , yang eksponensial dalam . 2 k O ( k 2 ) O ( ( 2 kn=2k3k2kO(k2)kO((2kk))k
j_random_hacker
1
@ orlp: Saya salah mengerti istilah "partisi multiplikasi", maaf. Terima kasih untuk kode Python. Untuk melihat mengapa algoritma Steve D tidak memparalelkan kode itu, pertimbangkan multiplicative_partitions(24), yang menghasilkan (antara lain) partisi [4, 3, 2]dan [6, 2, 2], yang (setelah membalik urutan untuk memberikan faktor prima terkecil eksponen tertinggi) sesuai dengan solusi dan , masing-masing. Algoritma Steve D tidak akan pernah mempertimbangkan solusi yang terakhir, karena telah menentukan bahwa subsolution . 2 5 3 1 5 1 2 3 3 2 = 72 < 2 5 3 1 = 962332512531512332=72<2531=96
j_random_hacker
-1

Calon yang memungkinkan untuk "bilangan bulat terkecil dengan n pembagi" adalah bilangan bulat dari formulir mana ≥ b ≥ c ... dan (a + 1) (b +1) (c + 1) ... = n.2Sebuah·3b·5c...

Jadi, Anda perlu menemukan semua cara untuk mengekspresikan n sebagai produk bilangan bulat ≥ 2 dalam urutan non-naik, dan menghitung dan memeriksa kandidat yang sesuai. Misalnya, ketika n = 16, 16 = 8 · 2 = 4 · 4 = 4 · 2 · 2 = 2 · 2 · 2 · 2, jadi kemungkinannya adalah , , , , dan yang terkecil adalah .27·323·3323·3·52·3·5·723·3·5=120

Jika n adalah produk dari dua bilangan prima p · q, p ≥ q, satu-satunya kandidat adalah dan , dan yang terakhir selalu lebih kecil .2halq-12hal-1·3q-1

Anda dapat mengetahui beberapa kondisi ketika mungkin ada faktor misalnya, dengan memeriksa apakah untuk beberapa prime x yang bukan merupakan faktor. Dalam contoh n = 16, ada faktor karena .2Sebuahb-12Sebuahb-1>2Sebuah-1·xb-12323<2·7

gnasher729
sumber
3
Maafkan saya, tetapi ini tidak menjawab pertanyaan saya sama sekali, itu hanya merangkum apa yang saya temukan dalam pertanyaan saya. Judulnya hanya itu: judul, bukan pertanyaan itu sendiri. Saya merasa seperti Anda hanya membaca judul sebelum menjawab. Pertanyaan sebenarnya ada di bagian bawah teks pertanyaan saya.
orlp
Itu dijawab dalam paragraf terakhir.
gnasher729
@ gnasher729 Itu jauh dari jawaban untuk pertanyaan "menghitung secara efisien", atau bahkan untuk "strategi optimal untuk penggabungan"
yo '