是否有算法只用很小的因子找到最接近的数字?

时间:2016-08-19 22:34:48

标签: algorithm factoring

我需要做一些实时DFT,当我可以将样本数量分解成小因子时,我使用的算法效率最高。

假设我有一个数字n和因子2, 3, 5。如何找到最接近的数字(与n比较),其素数因子化除了2,3,5以外没有数字?

n几乎总是低于50,000,所以粗暴强迫可能是一个好主意。

4 个答案:

答案 0 :(得分:4)

正好有265个数字在1到50000的范围内只能被2,3,5整除。所以你可以制作一个小桌子,然后在表格中查找答案。但是,在我的电脑上,找到给定数字的最近2-3-5可分数的数字平均需要6.5微秒,所以我说蛮力就足够了。

int isValid( int n )
{
    while ( n%2 == 0 )
        n /= 2;
    while ( n%3 == 0 )
        n /= 3;
    while ( n%5 == 0 )
        n /= 5;

    return n == 1;
}

int findBest( int n )
{
    for ( int i = 0; i < n; i++ )
    {
        if ( isValid(n+i) )
            return n+i;
        if ( isValid(n-i) )
            return n-i;
    }
    return 0;   // should never get here
}

int main( void )
{
    for ( int n = 1; n <= 50000; n++ )
        printf( "%d->%d\n", n,findBest(n) );
}

答案 1 :(得分:3)

成对因子的快速算法

这并没有解决所述的问题 - 给定一些整数x,它只能找到除了2和3之外没有因子的最接近的大于或等于的数字(或任何其他给定的配对因素)。但我觉得它很可爱,因为它在O(log x)时间和O(1)空间中运行,所以无论如何都可能有用!它在概念上与Bresenham线算法类似。在伪代码中:

  1. 从b = y = 2 ^ RoundUp(log2(x))开始,确保b = y> = x。
  2. 如果y < x然后设置y = y * 3并转到2。
  3. 如果y < b然后设置b = y。 (b记录到目前为止最佳候选人。)
  4. 如果y为奇数,则停止并返回b。
  5. 否则,请设置y = y / 2并转到2.
  6. 正确性

    为什么这样做?请注意,在任何时候,对于某些i,我们都有y = 2 ^ i * 3 ^ j,j> = 0,并且随着时间的推移,我只会减少,而j只会增加。我们在进入步骤2时保持的不变量是&#34;任何值z = 2 ^ a * 3 ^ b,其具有&gt; i或b&lt; j已知是无趣的(即无效或不比某些已经发现的解决方案更好),因此不需要考虑&#34; 。这在我们第一次到达步骤2时显然是正确的,因为y是2的幂并且已经> = x,因此任何数字z = 2 ^ a * 3 ^ b,其中a> 1。然后,我将至少2 * y(不管b)哪个比y差;并且没有整数z可以比y中的j = 0幂小3。

    (说明这个不变量的另一种方法是&#34;我们已经找到了最优解,或者它是一个数字z = 2 ^ a * 3 ^ b,其中&lt; = i和b&gt; = j &#34)

    如果步骤2中的条件,&#34; y&lt; x&#34;,满意,则y = 2 ^ i * 3 ^ j不是有效的解决方案,因为它太低了。更强烈地,很明显,对于任何a&lt; = i,2 ^ a * 3 ^ j也不是有效的解决方案,因为任何这样的解决方案至少与y一样低。所以现在我们知道(来自不变量)任何满足(a> i OR b i OR b i OR b i OR b i)为假,从而允许从OR中消除),这显然是相同的as(a&lt; = i AND b = j) - 但这正是我们刚刚建立第二种不感兴趣的条件,通过&#34; y&lt; X&#34;测试。因此,这确定了满足(a> i OR b

    在步骤5中递减i的理由几乎相同,但相反,所以我不会详细介绍它。稍有不同的是,如果我们进入第5步,而不是使用无效的解决方案,我们只需要一个至少与b中最佳解决方案一样高的解决方案(注意我们更新了b如果必要的,以便这将继续保持),所以它和每一个更高的解决方案(从这一点开始)对我们无趣。

    算法生成的y的每个值或者比任何先前生成的值少一个因子2或更多因子3,因此很清楚所有生成的y值都是不同的。前面段落中的推理确定,生成的唯一y值 not 是已被证明无趣的y值。因此,如果算法总是在有限的时间内停止,那么它是正确的。下一节将暗示确实如此。

    运行时间

    步骤5(具有将i减小1的效果)从不执行超过log2(x)+1次,因为我从此值开始或更少,没有其他任何影响i,当我达到0时,y将是奇怪的,导致步骤4终止该功能。但是,步骤2中的条件可以多少次将j增加1?

    最初y&gt; = x,因此实现y&lt;步骤2发射所需的x条件需要执行步骤5.但是一旦y&lt; x是通过执行步骤5来实现的,它会在下一次执行第2步时立即撤消:这是因为为了让步骤5完全执行,我们必须有y&gt; = x,如果我们划分y通过2(在该步骤5)然后将其乘以3(在下一步骤2),它必须至少与之前一样大,这意味着y> = x,反过来暗示如果没有在之间执行第5步,第2步将永远不会连续发射两次。因此,步骤2将最多触发与步骤5一样多次,即最多log2(x)+1次。这将算法的整体运行时限制为O(log x)。

    备注

    • 您可以通过将步骤1中的log2()替换为从1开始的循环并将其加倍直到它是&gt; = x来避免浮点运算。这是O(log x),因此不会影响时间复杂度。
    • 您可以使用任何其他因素。唯一真正的变化是,如果f是因素&#34;替换&#34;在代码中2,然后当y%f!= 0时,步骤4应该停止。

答案 2 :(得分:1)

我不确定是否有任何有效的解决方案.Below是用于找到最接近n的数字的蛮力方法。

    int diff=Integer.MAX_VALUE;
    int num=0;
    public void closestNumber(int n,int curr)
    {
        if(diff < Math.abs(n -curr) && curr  >= n)
        return;
        if(diff >= Math.abs(n -curr))
        {
            diff = Math.abs(n -curr);
            num=curr;
        }
        closestNumber(n,curr*2);
        closestNumber(n,curr*3);
        closestNumber(n,curr*5);

    }


closestNumber(n,1);

System.out.println("closest number: "+num);

答案 3 :(得分:0)

修改

下面的代码找到一个目标的最接近的数字,该数字可以被给定的一组因子中的至少一个数整除。它没有为明确的目标提供解决方案,以找到最接近的数字,该数字仅由一组给定的因子可以整除

<强>原始

可被2,3或5整除的一系列数字是OEIS A080671,并且具有简单的递归公式a(n + 22)= a(n)+30。此外,该系列方便地只有单整数间隙。这意味着您可以简单地确定您的数字是否位于其中一个间隙中,并选择下一个或上一个整数。

class NumberFinder
{
public:
    NumberFinder()
    {
        for (int i = 0; i < 2 * 3 * 5; i++)
        {
            bool hasSmallFactors =
                (i % 2 == 0) ||
                (i % 3 == 0) ||
                (i % 5 == 0);
            series.push_back(hasSmallFactors);
        }
    }

    int Find(int n)
    {
        int x = n % (2 * 3 * 5);
        if (series[x]) return n; // already good
        return n + 1; // guaranteed to be good
    }

private:
    vector<bool> series;
};

这也可以推广到任何一组期望的因素:

class NumberFinder
{
public:
    NumberFinder(vector<int> factors)
    {
        product = 1;
        for (auto factor : factors) product *= factor;
        for (int i = 0; i < product; i++)
        {
            bool hasSmallFactors = false;
            for (auto factor : factors)
            {
                if (i % factor == 0) hasSmallFactors = true;
            }
            series.push_back(hasSmallFactors);
        }
    }

    int Find(int n)
    {
        int lo = n;
        int hi = n;
        bool found = series[n % product];
        while (!found)
        {
            if (--lo < 0) lo = 0;
            hi++;
            found = series[hi % product] || series[lo % product];
        }
        if (series[lo % product]) return lo;
        return hi;
    }

private:
    int product;
    vector<bool> series;
};