我需要做一些实时DFT,当我可以将样本数量分解成小因子时,我使用的算法效率最高。
假设我有一个数字n
和因子2, 3, 5
。如何找到最接近的数字(与n
比较),其素数因子化除了2,3,5
以外没有数字?
n
几乎总是低于50,000
,所以粗暴强迫可能是一个好主意。
答案 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线算法类似。在伪代码中:
为什么这样做?请注意,在任何时候,对于某些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 在步骤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)。运行时间
备注
答案 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;
};