n 제수로 가장 작은 정수를 효율적으로 계산


9

이 문제를 해결하기 위해 먼저

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

여기서 은 제수 (반드시 소수 일 필요는 없음)입니다 . 경우 가장 작은 정수이다되도록 이어서,ϕ(m)mmϕ(m)=n

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

이제 우리는 선택해야 하도록을 최소이다. 에 대한 선택 은 사소합니다. 오름차순의 소수입니다.eiipieip

그러나 를 선택 첫 생각 은 틀 . 내가 생각 당신이 할 수 단순히 요인 , 종류와 작은 정수 예를 들면, 내림차순하고 대부분의 시간을이 작품 벌금 1. 빼기의 요인 의 약수는 다음과 같습니다einn=15

15=53
15=(4+1)(2+1)
미디엄=242=144

그러나 이것은 올바르지 않습니다 .=16

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

정답은 다음과 같습니다.

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

따라서 때때로 요소를 병합해야한다는 것이 분명합니다. 이때 인해 . 그러나 나는 깨끗하고 직접적인 병합 전략을 정확히 보지 못한다. 예를 들어, 항상 힘 으로 합쳐야한다고 생각할 수도 있지만, 이것은 사실이 아닙니다.71>222

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

나는 즉시 예를 생각할 수는 없지만 내 본능에 따르면 욕심 많은 접근 방식이 잘못된 힘을 먼저 합치면 실패 할 수 있다고 말합니다.

정답을 얻기 위해 이러한 권한을 병합하는 간단한 최적의 전략이 있습니까?


추가. 가능한 모든 병합을 확인하고 병합별로 최선을 수행하는 탐욕스러운 알고리즘은 에서 실패합니다 . 일련의 일대일 병합은 다음과 같습니다.n=3072

22315171111131171191231291311

23325171111131171191231291

25335171111131171191231

그러나 최적의 솔루션은 다음과 같습니다.

27335271111131171191

@ orlp : 내 제안은 : Fix (say, ) 및 fix (say, )입니다. 그런 다음 따라 을 최소화하려고합니다 . 따라서 고정 된 수 (소수)을 사용하여 특정 소수가 전체 최소값으로 표시되어야하는지 여부의 복잡성을 무시할 수 있습니다. 각 의 최소값을 찾은 다음 최소값을 취하십시오. n24m2k1log(2)+k2log(3)k1k2=24mm
Steve D

답변:


1

위의 의견을 기반으로 한 해결책이 있습니다. 나는 이것이 최적이라고 주장하지 않습니다.

아이디어는 을 고려하는 것이며 , 이는 "정확히 제수와 개의 고유 한 소수를 가진 가장 작은 양의 정수"로 정의됩니다 . 우리는 쉽게 관찰합니다 :T(n,m)nm

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

그리고 우리는 또한 재발이 있습니다.

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

마지막으로, 찾고자하는 수량은

min1ilog(n)T(n,i)

이를 위해 여기에 주어진 모든 숫자와 일치하는 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(n2logn)충분히 빠를 것 n수만에! (BTW : 동일한 알고리즘을 빼고 (최적화를 빼고) 같은 알고리즘을 작성하고 있었지만 먼저 도착했습니다.)
j_random_hacker

@ j_random_hacker : 예, 그 의견에 무슨 일이 있었는지 잘 모르겠습니다. 많은 의견이 있었고 이제 모두 사라졌습니다! 나는 실제로 시간의 복잡성에 대해 이야기하고 있었다. 나는 실제로 그것이 더 가까이 있다고 생각O(nlogn)그러나 제수의 수는 까다로운 기능입니다. 물론 위의 코드는 확실히 더 잘 최적화 될 수 있습니다 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 : "다중 파티션"이라는 용어를 잘못 이해했습니다. 죄송합니다. 파이썬 코드 감사합니다. Steve D의 알고리즘이 해당 코드를 병렬화하지 않는 이유를 확인하려면 multiplicative_partitions(24)파티션을 생성하는 파티션 [4, 3, 2][6, 2, 2](가장 작은 소수를 제공하기 위해 순서를 반대로 한 후 솔루션에 해당하는)를 고려하십시오.233251253151각각. Steve D의 알고리즘은 하위 솔루션을 이미 결정했기 때문에 후자의 솔루션을 고려하지 않습니다.2332=72<2531=96.
j_random_hacker

-1

"n 제수를 갖는 가장 작은 정수"에 대한 가능한 후보는 다음 형식의 정수입니다. 2a·3b·5c... 여기서 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 인 경우 가능성은 다음과 같습니다.27·3, 23·33, 23·3·5, 2·3·5·7가장 작은 23·3·5=120.

n이 2 개의 소수 p · q, p ≥ q의 곱이면 유일한 후보는 2pq12p1·3q1후자는 항상 더 작습니다.

요인이있을 수있는 상태를 파악할 수 있습니다 2ab1 예를 들어 2ab1>2a1·xb1소수가 아닌 중요한 x의 경우. 예제 n = 16에는 요인이 있습니다.23 때문에 23<2·7.


3
저를 용서하십시오. 그러나 이것은 내 질문에 전혀 답하지 않고 단지 내 질문에서 찾은 것을 요약합니다. 제목은이다 : 제목, 하지 질문 자체. 대답하기 전에 제목 만 읽은 것 같습니다. 실제 질문은 내 질문 텍스트의 맨 아래에 있습니다.
orlp

그것은 마지막 단락에서 대답됩니다.
gnasher729

@ gnasher729 이것은 "효율적으로 계산"또는 "최적의 병합 전략"이라는 질문에 대한 답변과는 거리가 멀다
yo '
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.