简体   繁体   English

找到不小于N的最小常数

[英]Find the smallest regular number that is not less than N

Regular numbers are numbers that evenly divide powers of 60. As an example, 60 2 = 3600 = 48 × 75, so both 48 and 75 are divisors of a power of 60. Thus, they are also regular numbers. 常数是均匀划分60的幂的数字。例如,60 2 = 3600 = 48×75,因此48和75都是60的幂的除数。因此,它们也是常规数。

This is an extension of rounding up to the next power of two . 这是四舍五入到下一个幂的延伸。

I have an integer value N which may contain large prime factors and I want to round it up to a number composed of only small prime factors (2, 3 and 5) 我有一个整数值N ,它可能包含大的素因子,我想把它四舍五入到只由小素因子组成的数字(2,3和5)

Examples: 例子:

  • f(18) == 18 == 2 1 * 3 2
  • f(19) == 20 == 2 2 * 5 1
  • f(257) == 270 == 2 1 * 3 3 * 5 1

What would be an efficient way to find the smallest number satisfying this requirement? 找到满足此要求的最小数字的有效方法是什么?

The values involved may be large, so I would like to avoid enumerating all regular numbers starting from 1 or maintaining an array of all possible values. 涉及的值可能很大,所以我想避免枚举从1开始的所有常规数字或维护所有可能值的数组。

One can produce arbitrarily thin a slice of the Hamming sequence around the n-th member in time ~ n^(2/3) by direct enumeration of triples (i,j,k) such that N = 2^i * 3^j * 5^k . 通过直接枚举三元组(i,j,k) ,可以在时间~ n^(2/3)围绕第n个成员产生任意薄的汉明序列切片,使得N = 2^i * 3^j * 5^k

The algorithm works from log2(N) = i+j*log2(3)+k*log2(5) ; 该算法从log2(N) = i+j*log2(3)+k*log2(5)起作用; enumerates all possible k s and for each, all possible j s, finds the top i and thus the triple (k,j,i) and keeps it in a "band" if inside the given "width" below the given high logarithmic top value (when width < 1 there can be at most one such i ) then sorts them by their logarithms. 枚举所有可能的k s,并且对于每个可能的j s,找到顶部i ,从而找到三元组(k,j,i)并且如果在给定的高对数顶部下面的给定“宽度”内,则将其保持在“带”中值(当width <1时,最多可以有一个这样的i )然后按它们的对数对它们进行排序。

WP says that n ~ (log N)^3 , ie run time ~ (log N)^2 . WP表示 n ~ (log N)^3 ,即运行时间~ (log N)^2 Here we don't care for the exact position of the found triple in the sequence, so all the count calculations from the original code can be thrown away: 这里我们不关心序列中找到的三元组的确切位置,因此原始代码中的所有计数计算都可以丢弃:

slice hi w = sortBy (compare `on` fst) b where       -- hi>log2(N) is a top value
  lb5=logBase 2 5 ; lb3=logBase 2 3                  -- w<1 (NB!) is log2(width)
  b  = concat                                        -- the slice
      [ [ (r,(i,j,k)) | frac < w ]                   -- store it, if inside width
        | k <- [ 0 .. floor ( hi   /lb5) ],  let p = fromIntegral k*lb5,
          j <- [ 0 .. floor ((hi-p)/lb3) ],  let q = fromIntegral j*lb3 + p,
          let (i,frac)=properFraction(hi-q) ;    r = hi - frac ]   -- r = i + q
                    -- properFraction 12.7 == (12, 0.7)

-- update: in pseudocode:
def slice(hi, w):
    lb5, lb3 = logBase(2, 5), logBase(2, 3)  -- logs base 2 of 5 and 3
    for k from 0 step 1 to floor(hi/lb5) inclusive:
        p = k*lb5
        for j from 0 step 1 to floor((hi-p)/lb3) inclusive:
           q = j*lb3 + p
           i = floor(hi-q)
           frac = hi-q-i                     -- frac < 1 , always
           r = hi - frac                     -- r == i + q
           if frac < w:
              place (r,(i,j,k)) into the output array
   sort the output array's entries by their "r" component
        in ascending order, and return thus sorted array

Having enumerated the triples in the slice, it is a simple matter of sorting and searching, taking practically O(1) time (for arbitrarily thin a slice) to find the first triple above N . 列举了切片中的三元组,这是一个简单的排序和搜索问题,实际上花费O(1)时间(对于任意薄的切片)来找到N之上的第一个三元组。 Well, actually, for constant width (logarithmic), the amount of numbers in the slice (members of the "upper crust" in the (i,j,k) -space below the log(N) plane) is again m ~ n^2/3 ~ (log N)^2 and sorting takes m log m time (so that searching, even linear, takes ~ m run time then). 那么,实际上,对于恒定宽度(对数),切片中的数字量(在log(N)平面下面的(i,j,k)空间中的“上地壳”的成员)也是m ~ n^2/3 ~ (log N)^2并且排序需要m log m时间(因此搜索,甚至线性,然后需要~ m运行时间)。 But the width can be made smaller for bigger N s, following some empirical observations; 但是,经过一些实证观察,对于更大的N s,宽度可以做得更小; and constant factors for the enumeration of triples are much higher than for the subsequent sorting anyway. 并且,三元组枚举的常数因子远远高于随后的排序。

Even with constant width (logarthmic) it runs very fast, calculating the 1,000,000-th value in the Hamming sequence instantly and the billionth in 0.05s. 即使有恒定的宽度(logarthmic)它运行速度非常快,计算汉明序列的百万个值即刻在十亿分在0.05秒。

The original idea of "top band of triples" is due to Louis Klauder, as cited in my post on a DDJ blogs discussion back in 2008. 最初的“三重乐队”的想法归功于路易斯·克劳德(Louis Klauder),正如在2008年关于DDJ博客讨论的帖子中所引用的那样。

update: as noted by GordonBGood in the comments , there's no need for the whole band but rather just about one or two values above and below the target. 更新:正如GordonBGood评论中所指出的那样 ,不需要整个乐队,而只需要在目标之上和之下的一两个值。 The algorithm is easily amended to that effect. 该算法很容易修改为该效果。 The input should also be tested for being a Hamming number itself before proceeding with the algorithm, to avoid round-off issues with double precision. 继续算法之前,还应该测试输入是汉明数,以避免双精度的舍入问题。 There are no round-off issues comparing the logarithms of the Hamming numbers known in advance to be different (though going up to a trillionth entry in the sequence uses about 14 significant digits in logarithm values, leaving only 1-2 digits to spare, so the situation may in fact be turning iffy there; but for 1-billionth we only need 11 significant digits). 有没有比较事先知道海明数的对数是不同的(尽管四舍五入问题上升到一万亿分项的顺序在对数值使用约14显著数字,只留下1-2位的空闲时间,所以事实上,情况可能会转向那里;但对于十亿分之一,我们只需要11位有效数字)。

update2: turns out the Double precision for logarithms limits this to numbers below about 20,000 to 40,000 decimal digits (ie 10 trillionth to 100 trillionth Hamming number). update2:结果是对数的双精度将其限制在低于大约20,000到40,000个十进制数字的数字(即10万亿到100万亿分之一的汉明数)。 If there's a real need for this for such big numbers, the algorithm can be switched back to working with the Integer values themselves instead of their logarithms, which will be slower. 如果对于如此大的数字确实需要这个,那么算法可以切换回使用Integer值本身而不是它们的对数,这将更慢。

Okay, hopefully third time's a charm here. 好的,希望第三次在这里有魅力。 A recursive, branching algorithm for an initial input of p, where N is the number being 'built' within each thread. 用于p的初始输入的递归分支算法,其中N是在每个线程内“构建”的数字。 NB 3a-c here are launched as separate threads or otherwise done (quasi-)asynchronously. 这里的NB 3a-c作为单独的线程启动或以其他方式异步完成(准)。

  1. Calculate the next-largest power of 2 after p, call this R. N = p. 在p之后计算下一个最大的2的幂,称之为R. N = p。

  2. Is N > R? N> R? Quit this thread. 退出这个帖子。 Is p composed of only small prime factors? p只由小素因子组成吗? You're done. 你完成了。 Otherwise, go to step 3. 否则,请转到步骤3。

  3. After any of 3a-c, go to step 4. 在3a-c中的任何一个之后,转到步骤4。

    a) Round p up to the nearest multiple of 2. This number can be expressed as m * 2. a)圆形p到最接近的2的倍数。该数字可表示为m * 2。
    b) Round p up to the nearest multiple of 3. This number can be expressed as m * 3. b)圆形p到最接近的3的倍数。该数字可表示为m * 3。
    c) Round p up to the nearest multiple of 5. This number can be expressed as m * 5. c)圆形p到最接近的5的倍数。该数字可表示为m * 5。

  4. Go to step 2, with p = m. 转到步骤2,p = m。

I've omitted the bookkeeping to do regarding keeping track of N but that's fairly straightforward I take it. 我省略了关于跟踪N的记账,但我认为这很简单。

Edit: Forgot 6, thanks ypercube. 编辑:忘了6,谢谢ypercube。

Edit 2: Had this up to 30, (5, 6, 10, 15, 30) realized that was unnecessary, took that out. 编辑2:如果这达到30,(5,6,10,15,30)意识到这是不必要的,那就把它拿出来。

Edit 3: (The last one I promise!) Added the power-of-30 check, which helps prevent this algorithm from eating up all your RAM. 编辑3 :(我承诺的最后一个!)添加了30次幂检查,这有助于防止此算法占用所有RAM。

Edit 4: Changed power-of-30 to power-of-2, per finnw's observation. 编辑4:根据finnw的观察,将30的幂变为2的幂。

Here's a solution in Python, based on Will Ness answer but taking some shortcuts and using pure integer math to avoid running into log space numerical accuracy errors: 这是Python中的一个解决方案,基于Will Ness的答案,但采用一些快捷方式并使用纯整数数学来避免遇到日志空间数值精度错误:

import math

def next_regular(target):
    """
    Find the next regular number greater than or equal to target.
    """
    # Check if it's already a power of 2 (or a non-integer)
    try:
        if not (target & (target-1)):
            return target
    except TypeError:
        # Convert floats/decimals for further processing
        target = int(math.ceil(target))

    if target <= 6:
        return target

    match = float('inf') # Anything found will be smaller
    p5 = 1
    while p5 < target:
        p35 = p5
        while p35 < target:
            # Ceiling integer division, avoiding conversion to float
            # (quotient = ceil(target / p35))
            # From https://stackoverflow.com/a/17511341/125507
            quotient = -(-target // p35)

            # Quickly find next power of 2 >= quotient
            # See https://stackoverflow.com/a/19164783/125507
            try:
                p2 = 2**((quotient - 1).bit_length())
            except AttributeError:
                # Fallback for Python <2.7
                p2 = 2**(len(bin(quotient - 1)) - 2)

            N = p2 * p35
            if N == target:
                return N
            elif N < match:
                match = N
            p35 *= 3
            if p35 == target:
                return p35
        if p35 < match:
            match = p35
        p5 *= 5
        if p5 == target:
            return p5
    if p5 < match:
        match = p5
    return match

In English: iterate through every combination of 5s and 3s, quickly finding the next power of 2 >= target for each pair and keeping the smallest result. 在英语中:迭代5s和3s的每个组合,快速找到每对的下一个2> =目标的幂并保持最小的结果。 (It's a waste of time to iterate through every possible multiple of 2 if only one of them can be correct). (如果只有其中一个是正确的,那么迭代每个可能的2的倍数是浪费时间)。 It also returns early if it ever finds that the target is already a regular number, though this is not strictly necessary. 如果它发现目标已经是常规数字,它也会提前返回,尽管这不是绝对必要的。

I've tested it pretty thoroughly, testing every integer from 0 to 51200000 and comparing to the list on OEIS http://oeis.org/A051037 , as well as many large numbers that are ±1 from regular numbers, etc. It's now available in SciPy as fftpack.helper.next_fast_len , to find optimal sizes for FFTs ( source code ). 我已经对它进行了相当彻底的测试,测试了0到51200000之间的每个整数,并与OEIS http://oeis.org/A051037上的列表进行了比较,还有许多大数字,这些数字是普通数字的±1等。 现在是在SciPy中以fftpack.helper.next_fast_len ,以找到FFT的最佳大小( 源代码 )。

I'm not sure if the log method is faster because I couldn't get it to work reliably enough to test it. 我不确定log方法是否更快,因为我无法让它足够可靠地工作来测试它。 I think it has a similar number of operations, though? 我认为它有相似数量的操作? I'm not sure, but this is reasonably fast. 我不确定,但速度相当快。 Takes <3 seconds (or 0.7 second with gmpy) to calculate that 2 142 × 3 80 × 5 444 is the next regular number above 2 2 × 3 454 × 5 249 +1 (the 100,000,000th regular number, which has 392 digits) 花费<3秒(或gmpy为0.7秒)来计算2 142 ×3 80 ×5 444是下一个常规数字2 2 ×3 454 ×5 249 +1(100,000,000个常规数字,其中有392位)

You want to find the smallest number m that is m >= N and m = 2^i * 3^j * 5^k where all i,j,k >= 0 . 你想找到m >= Nm = 2^i * 3^j * 5^k的最小数m ,其中所有i,j,k >= 0

Taking logarithms the equations can be rewritten as: 以对数表示方程式可以改写为:

 log m >= log N
 log m = i*log2 + j*log3 + k*log5

You can calculate log2 , log3 , log5 and logN to (enough high, depending on the size of N ) accuracy. 您可以将log2log3log5logN为(足够高,取决于N的大小)准确度。 Then this problem looks like a Integer Linear programming problem and you could try to solve it using one of the known algorithms for this NP-hard problem. 然后这个问题看起来像一个整数线性编程问题,你可以尝试使用这个NP难问题的已知算法之一解决它。

EDITED/CORRECTED: Corrected the codes to pass the scipy tests : EDITED / CORRECTED:更正了通过scipy测试的代码:

Here's an answer based on endolith's answer , but almost eliminating long multi-precision integer calculations by using float64 logarithm representations to do a base comparison to find triple values that pass the criteria, only resorting to full precision comparisons when there is a chance that the logarithm value may not be accurate enough, which only occurs when the target is very close to either the previous or the next regular number: 这是基于endolith的答案的答案 ,但几乎消除了长的多精度整数计算,通过使用float64对数表示来进行基本比较以找到通过标准的三元值,只有在有可能对数时才采用全精度比较值可能不够准确,仅当目标非常接近前一个或下一个常规数时才会出现:

import math

def next_regulary(target):
    """
    Find the next regular number greater than or equal to target.
    """
    if target < 2: return ( 0, 0, 0 )
    log2hi = 0
    mant = 0
    # Check if it's already a power of 2 (or a non-integer)
    try:
        mant = target & (target - 1)
        target = int(target) # take care of case where not int/float/decimal
    except TypeError:
        # Convert floats/decimals for further processing
        target = int(math.ceil(target))
        mant = target & (target - 1)

    # Quickly find next power of 2 >= target
    # See https://stackoverflow.com/a/19164783/125507
    try:
        log2hi = target.bit_length()
    except AttributeError:
        # Fallback for Python <2.7
        log2hi = len(bin(target)) - 2

    # exit if this is a power of two already...
    if not mant: return ( log2hi - 1, 0, 0 )

    # take care of trivial cases...
    if target < 9:
        if target < 4: return ( 0, 1, 0 )
        elif target < 6: return ( 0, 0, 1 )
        elif target < 7: return ( 1, 1, 0 )
        else: return ( 3, 0, 0 )

    # find log of target, which may exceed the float64 limit...
    if log2hi < 53: mant = target << (53 - log2hi)
    else: mant = target >> (log2hi - 53)
    log2target = log2hi + math.log2(float(mant) / (1 << 53))

    # log2 constants
    log2of2 = 1.0; log2of3 = math.log2(3); log2of5 = math.log2(5)

    # calculate range of log2 values close to target;
    # desired number has a logarithm of log2target <= x <= top...
    fctr = 6 * log2of3 * log2of5
    top = (log2target**3 + 2 * fctr)**(1/3) # for up to 2 numbers higher
    btm = 2 * log2target - top # or up to 2 numbers lower

    match = log2hi # Anything found will be smaller
    result = ( log2hi, 0, 0 ) # placeholder for eventual matches
    count = 0 # only used for debugging counting band
    fives = 0; fiveslmt = int(math.ceil(top / log2of5))
    while fives < fiveslmt:
        log2p = top - fives * log2of5
        threes = 0; threeslmt = int(math.ceil(log2p / log2of3))
        while threes < threeslmt:
            log2q = log2p - threes * log2of3
            twos = int(math.floor(log2q)); log2this = top - log2q + twos

            if log2this >= btm: count += 1 # only used for counting band
            if log2this >= btm and log2this < match:
                # logarithm precision may not be enough to differential between
                # the next lower regular number and the target, so do
                # a full resolution comparison to eliminate this case...
                if (2**twos * 3**threes * 5**fives) >= target:
                    match = log2this; result = ( twos, threes, fives )
            threes += 1
        fives += 1

    return result

print(next_regular(2**2 * 3**454 * 5**249 + 1)) # prints (142, 80, 444)

Since most long multi-precision calculations have been eliminated, gmpy isn't needed, and on IDEOne the above code takes 0.11 seconds instead of 0.48 seconds for endolith's solution to find the next regular number greater than the 100 millionth one as shown; 由于已经消除了大多数长时间的多精度计算,因此不需要gmpy,并且在IDEOne上,对于endolith的解决方案,上面的代码需要0.11秒而不是0.48秒,以找到大于第1百万个的下一个常规数字,如图所示; it takes 0.49 seconds instead of 5.48 seconds to find the next regular number past the billionth (next one is (761,572,489) past (1334,335,404) + 1 ), and the difference will get even larger as the range goes up as the multi-precision calculations get increasingly longer for the endolith version compared to almost none here. 找到下一个十亿分之一的下一个常规数字(下一个是(761,572,489)过去(1334,335,404) + 1 )需要0.49秒而不是5.48秒,随着范围的增加,差异会变得更大内部版本的精度计算越来越长,而这里几乎没有。 Thus, this version could calculate the next regular number from the trillionth in the sequence in about 50 seconds on IDEOne, where it would likely take over an hour with the endolith version. 因此,这个版本可以在IDEOne上大约50秒内从序列中的万亿分之一计算下一个常规数字,在那里使用endolith版本可能需要一个多小时。

The English description of the algorithm is almost the same as for the endolith version, differing as follows: 1) calculates the float log estimation of the argument target value (we can't use the built-in log function directly as the range may be much too large for representation as a 64-bit float), 2) compares the log representation values in determining qualifying values inside an estimated range above and below the target value of only about two or three numbers (depending on round-off), 3) compare multi-precision values only if within the above defined narrow band, 4) outputs the triple indices rather than the full long multi-precision integer (would be about 840 decimal digits for the one past the billionth, ten times that for the trillionth), which can then easily be converted to the long multi-precision value if required. 该算法的英文描述与endolith版本几乎相同,区别如下:1)计算参数目标值的浮点日志估计(我们不能直接使用内置log函数,因为范围可能是对于64位浮点表示来说太大了,2)比较日志表示值来确定估计范围内的限定值,该估计范围高于和低于目标值,只有大约两个或三个数字(取决于舍入),3 )仅在上面定义的窄带内比较多精度值,4)输出三重索引而不是全长多精度整数(对于十亿分之一的十分之一,将是大约840个十进制数字,是万亿分之一的十倍) ),如果需要,可以很容易地将其转换为长多精度值。

This algorithm uses almost no memory other than for the potentially very large multi-precision integer target value, the intermediate evaluation comparison values of about the same size, and the output expansion of the triples if required. 除了可能非常大的多精度整数目标值,大约相同大小的中间评估比较值以及三元组的输出扩展(如果需要)之外,该算法几乎不使用任何存储器。 This algorithm is an improvement over the endolith version in that it successfully uses the logarithm values for most comparisons in spite of their lack of precision, and that it narrows the band of compared numbers to just a few. 该算法是对endolith版本的改进,因为它成功地使用对数值进行大多数比较,尽管它们缺乏精确度,并且它将比较数字的范围缩小到几个。

This algorithm will work for argument ranges somewhat above ten trillion (a few minute's calculation time at IDEOne rates) when it will no longer be correct due to lack of precision in the log representation values as per @WillNess's discussion; 根据@WillNess的讨论,由于缺少日志表示值的精确度,该算法将适用于稍微超过10万亿(在IDEOne速率下几分钟的计算时间)的参数范围。 in order to fix this, we can change the log representation to a "roll-your-own" logarithm representation consisting of a fixed-length integer (124 bits for about double the exponent range, good for targets of over a hundred thousand digits if one is willing to wait); 为了解决这个问题,我们可以将日志表示更改为“自行滚动”对数表示,该表示由一个固定长度的整数组成(124位,大约是指数范围的两倍,对于超过十万个数字的目标,如果一个人愿意等待); this will be a little slower due to the smallish multi-precision integer operations being slower than float64 operations, but not that much slower since the size is limited (maybe a factor of three or so slower). 由于小的多精度整数运算比float64运算慢,所以这会慢一点,但由于大小有限(可能慢了三倍左右),因此速度要慢一些。

Now none of these Python implementations (without using C or Cython or PyPy or something) are particularly fast, as they are about a hundred times slower than as implemented in a compiled language. 现在这些Python实现中没有一个(不使用C或Cython或PyPy或其他东西)特别快,因为它们比在编译语言中实现的慢大约一百倍。 For reference sake, here is a Haskell version: 为了参考,这是一个Haskell版本:

{-# OPTIONS_GHC -O3 #-}

import Data.Word
import Data.Bits

nextRegular :: Integer -> ( Word32, Word32, Word32 )
nextRegular target
  | target < 2                   = ( 0, 0, 0 )
  | target .&. (target - 1) == 0 = ( fromIntegral lg2hi - 1, 0, 0 )
  | target < 9                   = case target of
                                     3 -> ( 0, 1, 0 )
                                     5 -> ( 0, 0, 1 )
                                     6 -> ( 1, 1, 0 )
                                     _ -> ( 3, 0, 0 )
  | otherwise                    = match
 where
  lg3 = logBase 2 3 :: Double; lg5 = logBase 2 5 :: Double
  lg2hi = let cntplcs v cnt =
                let nv = v `shiftR` 31 in
                if nv <= 0 then
                  let cntbts x c =
                        if x <= 0 then c else
                        case c + 1 of
                          nc -> nc `seq` cntbts (x `shiftR` 1) nc in
                  cntbts (fromIntegral v :: Word32) cnt
                else case cnt + 31 of ncnt -> ncnt `seq` cntplcs nv ncnt
          in cntplcs target 0
  lg2tgt = let mant = if lg2hi <= 53 then target `shiftL` (53 - lg2hi)
                      else target `shiftR` (lg2hi - 53)
           in fromIntegral lg2hi +
                logBase 2 (fromIntegral mant / 2^53 :: Double)
  lg2top = (lg2tgt^3 + 2 * 6 * lg3 * lg5)**(1/3) -- for 2 numbers or so higher
  lg2btm = 2* lg2tgt - lg2top -- or two numbers or so lower
  match =
    let klmt = floor (lg2top / lg5)
        loopk k mtchlgk mtchtplk =
          if k > klmt then mtchtplk else
          let p = lg2top - fromIntegral k * lg5
              jlmt = fromIntegral $ floor (p / lg3)
              loopj j mtchlgj mtchtplj =
                if j > jlmt then loopk (k + 1) mtchlgj mtchtplj else
                let q = p - fromIntegral j * lg3
                    ( i, frac ) = properFraction q; r = lg2top - frac
                    ( nmtchlg, nmtchtpl ) =
                      if r < lg2btm || r >= mtchlgj then
                        ( mtchlgj, mtchtplj ) else
                      if 2^i * 3^j * 5^k >= target then
                        ( r, ( i, j, k ) ) else ( mtchlgj, mtchtplj )
                in nmtchlg `seq` nmtchtpl `seq` loopj (j + 1) nmtchlg nmtchtpl
          in loopj 0 mtchlgk mtchtplk
    in loopk 0 (fromIntegral lg2hi) ( fromIntegral lg2hi, 0, 0 )


trival :: ( Word32, Word32, Word32 ) -> Integer
trival (i,j,k) = 2^i * 3^j * 5^k

main = putStrLn $ show $ nextRegular $ (trival (1334,335,404)) + 1 -- (1126,16930,40)

This code calculates the next regular number following the billionth in too small a time to be measured and following the trillionth in 0.69 seconds on IDEOne (and potentially could run even faster except that IDEOne doesn't support LLVM). 此代码计算在十亿分之一之后的下一个常规数字,在非常小的时间内进行测量,并且在IDEOne上以0.69秒的速度跟随万亿分之一(并且可能运行得更快,除了IDEOne不支持LLVM)。 Even Julia will run at something like this Haskell speed after the "warm-up" for JIT compilation. 在JIT编译的“热身”之后,甚至朱莉娅都会以类似Haskell的速度运行。

EDIT_ADD: The Julia code is as per the following: EDIT_ADD: Julia代码如下:

function nextregular(target :: BigInt) :: Tuple{ UInt32, UInt32, UInt32 }
    # trivial case of first value or anything less...
    target < 2 && return ( 0, 0, 0 )

    # Check if it's already a power of 2 (or a non-integer)
    mant = target & (target - 1)

    # Quickly find next power of 2 >= target
    log2hi :: UInt32 = 0
    test = target
    while true
        next = test & 0x7FFFFFFF
        test >>>= 31; log2hi += 31
        test <= 0 && (log2hi -= leading_zeros(UInt32(next)) - 1; break)
    end

    # exit if this is a power of two already...
    mant == 0 && return ( log2hi - 1, 0, 0 )

    # take care of trivial cases...
    if target < 9
        target < 4 && return ( 0, 1, 0 )
        target < 6 && return ( 0, 0, 1 )
        target < 7 && return ( 1, 1, 0 )
        return ( 3, 0, 0 )
    end

    # find log of target, which may exceed the Float64 limit...
    if log2hi < 53 mant = target << (53 - log2hi)
    else mant = target >>> (log2hi - 53) end
    log2target = log2hi + log(2, Float64(mant) / (1 << 53))

    # log2 constants
    log2of2 = 1.0; log2of3 = log(2, 3); log2of5 = log(2, 5)

    # calculate range of log2 values close to target;
    # desired number has a logarithm of log2target <= x <= top...
    fctr = 6 * log2of3 * log2of5
    top = (log2target^3 + 2 * fctr)^(1/3) # for 2 numbers or so higher
    btm = 2 * log2target - top # or 2 numbers or so lower

    # scan for values in the given narrow range that satisfy the criteria...
    match = log2hi # Anything found will be smaller
    result :: Tuple{UInt32,UInt32,UInt32} = ( log2hi, 0, 0 ) # placeholder for eventual matches
    fives :: UInt32 = 0; fiveslmt = UInt32(ceil(top / log2of5))
    while fives < fiveslmt
        log2p = top - fives * log2of5
        threes :: UInt32 = 0; threeslmt = UInt32(ceil(log2p / log2of3))
        while threes < threeslmt
            log2q = log2p - threes * log2of3
            twos = UInt32(floor(log2q)); log2this = top - log2q + twos

            if log2this >= btm && log2this < match
                # logarithm precision may not be enough to differential between
                # the next lower regular number and the target, so do
                # a full resolution comparison to eliminate this case...
                if (big(2)^twos * big(3)^threes * big(5)^fives) >= target
                    match = log2this; result = ( twos, threes, fives )
                end
            end
            threes += 1
        end
        fives += 1
    end
    result
end

Here's another possibility I just thought of: 这是我想到的另一种可能性:

If N is X bits long, then the smallest regular number RN will be in the range 如果NX位长,那么最小的规则数R≥N将在范围
[2 X-1 , 2 X ]

eg if N = 257 (binary 100000001 ) then we know R is 1 xxxxxxxx unless R is exactly equal to the next power of 2 (512) 例如,如果N = 257(二进制100000001 ),那么我们知道R1 xxxxxxxx除非R恰好等于2的下一个幂(512)

To generate all the regular numbers in this range, we can generate the odd regular numbers (ie multiples of powers of 3 and 5) first, then take each value and multiply by 2 (by bit-shifting) as many times as necessary to bring it into this range. 为了生成该范围内的所有常规数,我们可以首先生成奇数正则数(即3和5的幂的倍数),然后取每个值并乘以2(通过位移),根据需要多次它进入这个范围。

In Python: 在Python中:

from itertools import ifilter, takewhile
from Queue import PriorityQueue

def nextPowerOf2(n):
    p = max(1, n)
    while p != (p & -p):
        p += p & -p
    return p

# Generate multiples of powers of 3, 5
def oddRegulars():
    q = PriorityQueue()
    q.put(1)
    prev = None
    while not q.empty():
        n = q.get()
        if n != prev:
            prev = n
            yield n
            if n % 3 == 0:
                q.put(n // 3 * 5)
            q.put(n * 3)

# Generate regular numbers with the same number of bits as n
def regularsCloseTo(n):
    p = nextPowerOf2(n)
    numBits = len(bin(n))
    for i in takewhile(lambda x: x <= p, oddRegulars()):
        yield i << max(0, numBits - len(bin(i)))

def nextRegular(n):
    bigEnough = ifilter(lambda x: x >= n, regularsCloseTo(n))
    return min(bigEnough)

I wrote a small c# program to solve this problem. 我写了一个小的c#程序来解决这个问题。 It's not very optimised but it's a start. 它不是很优化,但它是一个开始。 This solution is pretty fast for numbers as big as 11 digits. 对于大到11位的数字,此解决方案非常快。

private long GetRegularNumber(long n)
{
    long result = n - 1;
    long quotient = result;

    while (quotient > 1)
    {
        result++;
        quotient = result;

        quotient = RemoveFactor(quotient, 2);
        quotient = RemoveFactor(quotient, 3);
        quotient = RemoveFactor(quotient, 5);
    }

    return result;
}

private static long RemoveFactor(long dividend, long divisor)
{
    long remainder = 0;
    long quotient = dividend;
    while (remainder == 0)
    {
        dividend = quotient;
        quotient = Math.DivRem(dividend, divisor, out remainder);
    }
    return dividend;
}

You know what? 你知道吗? I'll put money on the proposition that actually, the 'dumb' algorithm is fastest. 我会把钱投入到这个命题上,实际上,'哑'算法是最快的。 This is based on the observation that the next regular number does not, in general, seem to be much larger than the given input. 这是基于以下观察:下一个常规数字通常看起来不比给定的输入大得多。 So simply start counting up, and after each increment, refactor and see if you've found a regular number. 因此,只需开始计数,并在每次增量后重构,看看你是否找到了常规数字。 But create one processing thread for each available core you have, and for N cores have each thread examine every Nth number. 但是为每个可用的核心创建一个处理线程,对于N个核心,每个线程检查每个第N个数字。 When each thread has found a number or crossed the power-of-2 threshold, compare the results (keep a running best number) and there you are. 当每个线程找到一个数字或超过2的幂阈值时,比较结果(保持运行中的最佳数字),然后就可以了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 查找数组在小于O(n ^ 2)内重复的次数 - Find the number of times a number is repeated in an array in less than O(n^2) 如何找到小于O(n)的数组中出现次数最多的数字? - How to find a number that occurs the most in an array in less than O(n)? 在小于O(n)比较中找到3个最小元素的时间复杂度 - Time complexity of finding 3 smallest elements in less than O(n) comparisons 如何找到可以除以所有数字 1:n 且没有余数的最小数字? - How do I find the smallest number than can be divided by all numbers 1:n with no remainder? 给定数N,找到最小的偶数E,使得E&gt; N. - Given a number N, find the smallest even number E such that E > N 查找集合中的数字加起来最接近 n 且小于 n 的数字 - Find the numbers in a collection that add up to the number closest to n which is less than n 查找数组中第n1个最小数到第n2个最小数的快速算法 - Fast algorithm to find the n1 th smallest number to n2 th smallest number in an array 找到可被1到N之间所有数字整除的最小数字,剩余数= 0 - Find the smallest number that is divisible by all the number between 1 to N, Remainder = 0 如何减少在不到1秒的时间内从连续数字找到第n位的时间 - How to reduce time to find the n-th place from consecutive digits number for less than 1 second 如何在小于O(n)时间复杂度的情况下找到第N个Armstrong数? - How to find Nth Armstrong number in less than O(n) time complexity?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM