如何快速评估零集?

时间:2016-05-02 07:39:29

标签: c++ c performance sorting search

最近code golfing post询问了在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}形成 arithmetic progression ,从而改变范围,然后使用一些bitwise tricks

if (((n - 6) & 14) + 6 == n)

导致更短(可能确实更有效)的实现,如John Bollinger的answered

现在我问的是

的类似优雅(并且希望同样有效)的实现

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是否是排序数组的成员时,我们做得更好。

6 个答案:

答案 0 :(得分:25)

if ((n > 2) && (2187 % (n - 2) == 0))

检查(n - 2)是否为3并且小于或等于2187(3为7的幂)

作为概括,要检查是否有任何无符号整数n是素数k的幂,您可以检查n是否除以k的最大幂{可以存储在无符号整数中。

答案 1 :(得分:8)

这与recognizing a power of three非常相似,您可以调整this solution

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);
}

答案 2 :(得分:5)

  

提示:这次数字a[k]形成几何级数:a[k]=2+3^k

n = 2 + 3^k
n - 2 = 3^k

(n - 2) / 3^k = 1
(n - 2) % 3^k = 0

k = 0 ~ n-2 = 3^0 = 1, n = 3
k = 1 ~ n-2 = 3^1 = 3, n = 5
k = 2 ~ n-2 = 3^3 = 9, n = 11

if (n > 2 && isPow(n-2, 3))

定义函数isPow(x,y)

bool isPow(unsigned long x, unsigned int y)
{
    while (x % y == 0)
    {
        x /= y;
    }

    return x == 1;
}

n = n  ~ n-2 = 3^k/3 = 3^(k-1)/3 = .. = 3^1/3 = 1%3 != 0
n = 11 ~ n-2 = 9/3 = 3/3 = 1%3 != 0
n = 5  ~ n-2 = 3/3 = 1%3 != 0
n = 3  ~ n-2 = 1%3 != 0

同样我们可以扣除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

答案 3 :(得分:3)

在相关帖子中发现了类似的问题: 您可以使用std::find

bool found = (std::find(my_var.begin(), my_var.end(), my_variable) != my_var.end());
// my_var is a list of elements.

(请确保包含&lt; algorithm&gt;)。

对于这类东西,在99%的情况下,有一个图书馆为你完成这项工作。

答案 4 :(得分:3)

执行此类操作的一种非常有效的方法是使用集合,尤其是unordered_set。作为哈希表,搜索是平均常数,最差线性。它也比一系列条件更优雅,并且可以很好地扩展。

只需插入要比较的值,然后count设置中的值。如果找到,它是您测试的值之一。

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";

答案 5 :(得分:0)

以下是我提出的建议:

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
}

由于数字n是一个小整数,浮点不准确应该不是问题。

当然,它不会像整数运算一样快,它不适合表达式,它可能比某种数组搜索更快,特别是如果你在哪里增加最大值n值。

编辑我测试了我的代码(未启用优化),平均而言(对于所有小于或等于2189的n),我的版本耗时185.849 ns,而||版本占用了116.546 ns ,(多次运行我的程序产生类似的结果)所以这不会更快。 同样出于某些奇怪的原因,它会在b时将false设置为n == 245,(它应该是真的)。​​