C++ 如何快速评估零集?
最近的一篇文章询问了在C语言中快速实现以下内容的可能性(假设C++ 如何快速评估零集?,c++,c,performance,sorting,search,C++,C,Performance,Sorting,Search,最近的一篇文章询问了在C语言中快速实现以下内容的可能性(假设n是一个无符号整数): if(n==6 | n==8 | n==10 | n==12 | n==14 | n==16 | n==18 | n==20) 一种可能的简化是观察数字a[]={6,8,10,12,14,16,18,20}形成一个,因此移动范围,然后使用一些 if((n-6&14)+6==n) 这将导致一个更短(甚至可能更高效)的实现,正如约翰·博林格(John Bollinger)所言 现在我要问的是,与之类似的优雅(希望同样
n
是一个无符号整数):
if(n==6 | n==8 | n==10 | n==12 | n==14 | n==16 | n==18 | n==20)
一种可能的简化是观察数字a[]={6,8,10,12,14,16,18,20}
形成一个,因此移动范围,然后使用一些
if((n-6&14)+6==n)
这将导致一个更短(甚至可能更高效)的实现,正如约翰·博林格(John Bollinger)所言
现在我要问的是,与之类似的优雅(希望同样高效)的实现是什么
if(n==3 | n==5 | n==11 | n==29 | n==83 | n==245 | n==731 | n==2189)
提示:这次数字a[k]
形成几何级数:a[k]=2+3^k
我想在一般情况下,对数字
a[k]
进行排序,然后进行对数搜索,以测试n
是否是已排序数组的成员。以下是我得出的结论:
bool b;
if (n >= 3 && n <= 2189)
{
double d = std::log(n - 2)/std::log(3); // log₃(n - 2)
b = std::trunc(d) == d; // Checks to see if this is an integer
// If ‘b’ then ‘n == a[log₃(n - 2)]’
}
else b = false;
if (b)
{
// your code
}
bool-b;
如果(n>=3&&n在相关帖子中发现类似问题:
您可以使用std::find
bool found = (std::find(my_var.begin(), my_var.end(), my_variable) != my_var.end());
// my_var is a list of elements.
(一定要包括在内)
对于这类东西,99%的情况下,有一个图书馆为你做这项工作
if ((n > 2) && (2187 % (n - 2) == 0))
检查(n-2)
是否是3的幂,并且小于或等于2187
(3到7的幂)
作为推广,要检查任何无符号整数n
是否是素数k
的幂,可以检查n
是否除以可存储在无符号整数中的k
的最大幂
提示:这次数字a[k]
形成几何级数:a[k]=2+3^k
函数定义为isPow(x,y)
同样,我们可以扣除k
int k = findK(n-2, 3);
int findK(unsigned long x, unsigned int y)
{
unsigned int k = 0;
while (x % y == 0)
{
x /= y;
k++;
}
if (x == 1) return k;
else return (-1);
}
n - 2 = 3 * 3^(k-1) for k > 0
(n - 2) / 3 = 3^(k-1)
(n - 2) / 3 / 3 = 3^(k-2)
(n - 2) / 3 / 3 / 3 = 3^(k-3)
(n - 2) / 3 / 3 / 3 / 3 = 3^(k-4)
..
(n - 2) / 3 / 3 / ..i times = 3^(k-i)
..
(n - 2) / 3 / 3 / ..k times = 3^0 = 1
做这类事情的一个非常有效的方法是使用一个集合,特别是一个。作为一个哈希表,搜索是平均常数,最差线性的。它也比一系列条件和比例优雅得多
只需插入要比较的值,然后对集合中的值进行计数。如果找到,则它是您测试的值之一
std::unordered_set< int > values;
values.insert( 3 );
values.insert( 5 );
values.insert( 11 );
values.insert( 29 );
values.insert( 83 );
values.insert( 245 );
values.insert( 731 );
values.insert( 2189 );
...
if( values.count( input ) )
std::cout << "Value is in set.\n";
else
std::cout << "Value is NOT in set.\n";
std::无序_集值;
插入(3);
插入(5);
插入(11);
插入(29);
插入(83);
插入(245);
插入(731);
插入(2189);
...
if(值.计数(输入))
std::cout这与非常相似,您可以进行调整,例如:
bool测试(无符号x){
x-=2;
如果(x>2187)
返回0;
如果(x>243)
x*=0xd2b3183b;
返回x如果这是一个时间与空间的折衷,那么我将分配一个2189字的缓冲区,除了特定的位置(3、5、11等等)之外,它将填充零,并简单地执行数组查找(这是非常快的,因为它是硬件实现的)。这不是很优雅,但会以空间性能为代价给你带来最佳的时间性能。这样的问题不应该发布在?FWIW上吗?((n-6)和14)+6==n
可以简化为(n-6)| 14==14
@UriBrecher-可能是2k值。对于一般情况,庞大的查找表对缓存的影响可能是不可忽略的。您是追求原始速度还是优雅的C代码?它们可能不同。除法(包括模)操作是现代处理器上最慢的操作,所以Suu的答案看起来很酷,但可能不会比一系列的比较快。如果你真的关心速度,那么在决定之前检查你的编译器的输出和基准。也考虑到分支在紧循环中是慢的。如果你的优化器不是SM。艺术足以自动完成这个替换(Clang是,其他不是),考虑使用位或代替逻辑或。如果“代码> n<2</代码>BTW,计算单个<代码>日志< /代码>可能比原始表达式多。至少对于<代码>日志(3)
,因为问题是关于性能的,所以您可能应该在静态变量中维护它(即,只计算一次)@barakmanos谢谢我修复了它。嗯,我没想到,也许我会测试它…再想想,log
可能返回-inf
ofNaN
,在这种情况下,接下来的测试可能会起作用。静态双log3=std::log(3)
。为什么在没有优化的情况下对其进行测试?这几乎让我们无法确定哪一个更快。std::find
具有线性运行时。我认为这算不上可能的最快实现……对于8个值,这可能很快-许多CPU都有一个带计数器的重复比较操作来限制性能数据量的搜索,它没有太多的数据缓存。如果这比二进制搜索更让我吃惊。@ TonyD,我认为在一个C++会议上有一个演示,在这里,它实际上是被测量的,而对于小的集合来说,线性搜索更快。然而,这个问题也要求一般的情况,他正在计算。汇编程序指令。在这种人工情况下,我认为线性搜索不符合条件。@Matsmath在这个专业中,我们是用事实而不是感情来操作的。@Matsmath为什么包含库会影响性能?函数调用可能还是内联的。性能不会与手写循环不同。这是一个很好的answ呃,但是你应该把“小于或等于2187”改为“小于或等于2189”。另外,你应该添加一个n>2
@thiru很棒的工作!(我可以通过测试确认你的解决方案是正确的,而且速度更快(1.14703纳秒,而不是3.34201纳秒)。注意除法运算(包括模运算)基本上是现代处理器上最慢的指令,速度要慢得多
int k = findK(n-2, 3);
int findK(unsigned long x, unsigned int y)
{
unsigned int k = 0;
while (x % y == 0)
{
x /= y;
k++;
}
if (x == 1) return k;
else return (-1);
}
n - 2 = 3 * 3^(k-1) for k > 0
(n - 2) / 3 = 3^(k-1)
(n - 2) / 3 / 3 = 3^(k-2)
(n - 2) / 3 / 3 / 3 = 3^(k-3)
(n - 2) / 3 / 3 / 3 / 3 = 3^(k-4)
..
(n - 2) / 3 / 3 / ..i times = 3^(k-i)
..
(n - 2) / 3 / 3 / ..k times = 3^0 = 1
std::unordered_set< int > values;
values.insert( 3 );
values.insert( 5 );
values.insert( 11 );
values.insert( 29 );
values.insert( 83 );
values.insert( 245 );
values.insert( 731 );
values.insert( 2189 );
...
if( values.count( input ) )
std::cout << "Value is in set.\n";
else
std::cout << "Value is NOT in set.\n";
bool test(unsigned x) {
x -= 2;
if (x > 2187)
return 0;
if (x > 243)
x *= 0xd2b3183b;
return x <= 243 && ((x * 0x71c5) & 0x5145) == 0x5145;
}
bool test2(unsigned x) {
x -= 2;
return x <= 2187 && (((x * 0x4be55) & 0xd2514105) == 5);
}