คำนวณจำนวนเต็มขนาดเล็กที่สุดอย่างมีประสิทธิภาพด้วยตัวหาร n


9

เพื่อที่จะแก้ไขปัญหานี้ฉันแรกสังเกตเห็นว่า

φ(พี1อี1 พี2อี2 พีkอีk)=(อี1+1)(อี2+1)(อีk+1)

ที่ไหน φ(ม.) คือจำนวนตัวหาร (ไม่จำเป็นต้องมี) ของ ม.. ถ้าม. เป็นจำนวนเต็มเล็กที่สุดเช่นนั้น φ(ม.)=nจากนั้น

φ(ม.)=n
(อี1+1)(อี2+1)(อีk+1)=n

ตอนนี้เราต้องเลือก อีผม ดังนั้น Πผมพีผมอีผมน้อยที่สุด ทางเลือกสำหรับพี เป็นเรื่องเล็กน้อย - พวกเขาเป็นเพียงช่วงเวลาที่อยู่ในลำดับขึ้น

อย่างไรก็ตามความคิดแรกของฉันสำหรับการเลือก อีผมไม่ถูกต้อง ฉันคิดว่าคุณสามารถแยกตัวประกอบnเรียงลำดับปัจจัยจากมากไปหาน้อยและลบ 1 ส่วนใหญ่ใช้งานได้ดีเช่นจำนวนเต็มเล็กที่สุดด้วย n=15 ตัวหารคือ:

15=53
15=(4+1)(2+1)
ม.=2432=144

แต่นี่ไม่ถูกต้องสำหรับ n=16:

16=2222
16=(1+1)(1+1)(1+1)(1+1)
ม.=21315171=210

ในขณะที่คำตอบที่ถูกต้องคือ:

16=(3+1)(1+1)(1+1)
ม.=233151=120

ดังนั้นจึงเป็นที่ชัดเจนว่าบางครั้งเราจำเป็นต้องรวมปัจจัย ในกรณีนี้เพราะ71>22. แต่ฉันไม่เห็นกลยุทธ์การรวมที่สะอาดและตรง ตัวอย่างเช่นบางคนอาจคิดว่าเราต้องรวมเข้ากับ2 อำนาจ แต่นี่ไม่เป็นความจริง:

1552=(96+1)(1+1)(1+1)(1+1)(1+1)
ม.=296315171111>296335171

ฉันไม่สามารถคิดถึงตัวอย่างได้ทันที แต่สัญชาตญาณของฉันบอกว่าวิธีโลภบางอย่างอาจล้มเหลวหากพวกเขารวมพลังที่ผิดก่อน

มีกลยุทธ์ที่ง่ายที่สุดสำหรับการรวมพลังเหล่านี้เพื่อให้ได้คำตอบที่ถูกต้องหรือไม่?


ภาคผนวก อัลกอริทึมโลภที่ตรวจสอบการผสานที่เป็นไปได้ทั้งหมดและดำเนินการอย่างดีที่สุดบนพื้นฐานการผสานแบบผสานโดยล้มเหลวn=3072. ชุดของการผสานแบบหนึ่งต่อหนึ่งคือ:

22315171111131171191231291วันที่ 311

23325171111131171191231291

25335171111131171191231

อย่างไรก็ตามทางออกที่ดีที่สุดคือ:

27335271111131171191

@orlp: ข้อเสนอแนะของฉันคือ: แก้ไข n (พูด, 24) และแก้ไข ม. (พูด, 2) จากนั้นคุณกำลังพยายามย่อเล็กสุดk1เข้าสู่ระบบ(2)+k2เข้าสู่ระบบ(3)ภายใต้ k1k2=24. ดังนั้นด้วยการทำงานกับหมายเลขที่แน่นอนม.(จากจำนวนเฉพาะ) คุณสามารถเพิกเฉยต่อความยุ่งเหยิงว่านายกคนใดคนหนึ่งควรจะปรากฏในระดับต่ำสุดทั่วโลกหรือไม่ คุณค้นหาขั้นต่ำสำหรับแต่ละม.จากนั้นใช้เวลาน้อยที่สุด
Steve D

คำตอบ:


1

นี่คือวิธีแก้ไขตามความคิดเห็นของฉันด้านบน ฉันไม่ได้อ้างสิทธิ์นี้เป็นสิ่งที่ดีที่สุด

ความคิดคือการพิจารณา T(n,ม.)ซึ่งเรานิยามว่า "จำนวนเต็มบวกที่เล็กที่สุดด้วย n ตัวหารและ ม. ปัจจัยสำคัญเฉพาะ "เราทำการสังเกตได้ง่าย:

T(n,1)=2n-1T(2ม.,ม.)=พี1พี2พีม.

และเราก็มีการเกิดขึ้นอีก:

T(n,ม.)=นาทีd|n[T(nd,ม.-1)พีม.d-1]

ในที่สุดปริมาณที่คุณต้องการคือ

นาที1ผมเข้าสู่ระบบ(n)T(n,ผม)

ในตอนท้ายนี่คือรหัส Python บางส่วนที่เห็นด้วยกับตัวเลขทั้งหมดที่คุณให้ไว้ด้านบน 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))

ความคิดเห็นที่คุณอ้างถึงดูเหมือนว่าถูกลบไปแล้วน่าเสียดาย แต่นี่เป็นสิ่งที่ดีที่สุด (ในแง่ของการคำนวณจำนวนเต็มเล็กที่สุดเท่าที่จะเป็นไปได้ด้วย nปัจจัย). มันเป็นการเพิ่มประสิทธิภาพของความซับซ้อนของเวลาที่คุณไม่แน่ใจหรือไม่? ฉันไม่ทราบขอบเขตที่ จำกัด สำหรับจำนวนตัวหารของจำนวนเต็มnแต่ถึงแม้จะมีขอบเขตที่มองโลกในแง่ร้ายมาก O(n) อัลกอริทึมของคุณเท่านั้น O(n2เข้าสู่ระบบn)ซึ่งควรจะเร็วพอสำหรับ nในหมื่น! (BTW: ฉันกำลังเขียนอัลกอริทึมเดียวกัน (ลบด้วยการเพิ่มประสิทธิภาพบางอย่าง) แต่คุณมาถึงที่นั่นก่อนทำได้ดีมาก!)
j_random_hacker

@j_random_hacker: ใช่ไม่แน่ใจว่าเกิดอะไรขึ้นกับความคิดเห็นเหล่านั้น: มีหลายคนและตอนนี้พวกเขาทั้งหมดหายไปแล้ว! แน่นอนฉันกำลังพูดถึงความซับซ้อนของเวลา จริง ๆ แล้วฉันคิดว่ามันน่าจะใกล้เคียงกว่านี้O(nเข้าสู่ระบบn)แต่จำนวนตัวหารเป็นฟังก์ชันที่ยุ่งยาก แน่นอนว่าโค้ดข้างต้นสามารถปรับให้เหมาะสมได้ดีกว่า: powersetตัวอย่างเช่นไม่มีการซ้ำซ้อน
Steve D

ฉันเชื่อว่าสิ่งนี้ง่ายกว่าในการนำไปใช้อย่างมีประสิทธิภาพโดยใช้การเขียนโปรแกรมแบบไดนามิก: gist.github.com/orlp/0fbb7784782712bc7c411aa58a188143ฉันไม่พอใจกับเคล็ดลับลอการิทึมโดยวิธี - ความแม่นยำที่ จำกัด ในบางจุด ที่ถูกกล่าวว่าฉันไม่เชื่อว่านี่จะเร็วกว่าการสร้างพาร์ทิชันหลายตัว ในความเป็นจริงฉันเชื่อว่านั่นคือสิ่งที่มันปลอมตัว!
orlp

หลังจากอ่านความคิดเห็นของ @ orlp และรหัสของคุณอย่างใกล้ชิดยิ่งขึ้นฉันคิดว่ามันเป็นเรื่องสำคัญที่ความซับซ้อนของเวลา (และการปฏิบัติจริง) เพื่อเปลี่ยนfor factor_list in powerset(factors)เป็นสิ่งที่สร้างตัวหารที่แตกต่างกันnอย่างแน่นอนเพียงครั้งเดียว ด้วยวิธีนี้พูดn=2k3kเมื่อคุณพิจารณาวิธีแก้ไขปัญหาที่มีอย่างแรก 2k ช่วงเวลาเป็นปัจจัยสำคัญที่แตกต่างกันของพวกเขาคุณจะทำ O(k2) ทำงานที่ไม่ใช่แบบเรียกซ้ำได้ O((2kk))ซึ่งเป็นเลขชี้กำลังใน k.
j_random_hacker

1
@ orlp: ฉันเข้าใจผิดคำว่า "พาร์ทิชัน multiplicative" ขอโทษ ขอบคุณสำหรับรหัสหลาม เพื่อดูว่าทำไมอัลกอริธึมของ Steve D ไม่ขนานรหัสนั้นให้พิจารณาmultiplicative_partitions(24)ซึ่งสร้างพาร์ติชั่น ( [4, 3, 2]และอื่น ๆ ) และ[6, 2, 2]ซึ่ง (หลังจากย้อนกลับคำสั่งเพื่อให้ปัจจัยสำคัญที่เล็กที่สุดกับเลขชี้กำลังสูงสุด) สอดคล้องกับวิธีแก้ปัญหา233251 และ 253151ตามลำดับ อัลกอริทึมของ Steve D จะไม่พิจารณาโซลูชันหลังเนื่องจากได้พิจารณาแล้วว่าการยุบตัว2332=72<2531=96.
j_random_hacker

-1

ตัวเลือกที่เป็นไปได้สำหรับ "จำนวนเต็มน้อยที่สุดที่มีตัวหาร n" เป็นจำนวนเต็มของแบบฟอร์ม 2a·3·5... โดยที่ a ≥ b ≥ c ... และ (a + 1) (b + 1) (c + 1) ... = n

ดังนั้นคุณต้องหาวิธีทั้งหมดในการแสดง n เป็นผลคูณของจำนวนเต็ม≥ 2 ในลำดับที่ไม่ขึ้นและคำนวณและตรวจสอบผู้สมัครที่เกี่ยวข้อง ตัวอย่างเช่นเมื่อ n = 16, 16 = 8 · 2 = 4 · 4 = 4 · 2 · 2 = 2 · 2 · 2 · 2 · 2 ดังนั้นความเป็นไปได้คือ27·3, 23·33, 23·3·5, 2·3·5·7และที่เล็กที่สุดคือ 23·3·5=120.

ถ้า n เป็นผลผลิตของสองช่วงเวลา p · q, p ≥ q ผู้สมัครคนเดียวคือ 2พีQ-1 และ 2พี-1·3Q-1และหลังจะเล็กกว่าเสมอ

คุณสามารถหาเงื่อนไขบางอย่างเมื่ออาจมีปัจจัย 2a-1 ตัวอย่างเช่นโดยการตรวจสอบว่า 2a-1>2a-1·x-1สำหรับนายกเอกที่ไม่ได้เป็นปัจจัย ในตัวอย่าง n = 16 มีปัจจัย23 เพราะ 23<2·7.


3
ยกโทษให้ฉัน แต่นี่ไม่ได้ตอบคำถามของฉันเลยมันแค่สรุปสิ่งที่ฉันพบในคำถามของฉัน ชื่อเป็นเพียงแค่: ชื่อไม่ใช่คำถามเอง ฉันรู้สึกว่าคุณอ่านชื่อก่อนตอบเท่านั้น คำถามจริงอยู่ที่ด้านล่างของข้อความคำถามของฉัน
orlp

นั่นคือคำตอบในวรรคสุดท้าย
gnasher729

@ gnasher729 ไม่ได้ตอบคำถาม "การคำนวณอย่างมีประสิทธิภาพ" หรือแม้แต่ "กลยุทธ์ที่ดีที่สุดสำหรับการรวม"
yo '
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.