最小正数的最快/最快算法

时间:2008-12-26 15:26:52

标签: c++ algorithm optimization

简单的问题 - 在c ++中,获得两个数字(u0和u1)中哪一个是最小正数的最佳方法是什么? (那仍然有效)

我尝试的每一种方式都涉及大的if语句或复杂的条件语句。

谢谢, 丹

这是一个简单的例子:

bool lowestPositive(int a, int b, int& result)
{
    //checking code
    result = b;
    return true;
}


lowestPositive(5, 6, result);

15 个答案:

答案 0 :(得分:15)

如果值以二进制补码表示,则

result = ((unsigned )a < (unsigned )b) ? a : b;

将起作用,因为二进制补码中的负值在被视为无符号时比正值更大。与杰夫的答案一样,这假设至少有一个值为正值。

return result >= 0;

答案 1 :(得分:12)

我更喜欢清晰度而非紧凑性:

bool lowestPositive( int a, int b, int& result )
{
   if (a > 0 && a <= b) // a is positive and smaller than or equal to b
      result = a;
   else if (b > 0) // b is positive and either smaller than a or a is negative
      result = b;
   else
      result = a; // at least b is negative, we might not have an answer

   return result > 0;  // zero is not positive
}

答案 2 :(得分:3)

unsigned int mask = 1 << 31;
unsigned int m = mask;
while ((a & m) == (b & m)) {
  m >>= 1;
}
result = (a & m) ? b : a;
return ! ((a & mask) && (b & mask));
编辑:认为这不是那么有趣所以我删除了它。但是在第二个想法,只是留在这里好玩:)这可以被视为道格答案的转储版本:))

答案 3 :(得分:3)

可能让我受到限制,但只是为了踢,这里是没有任何比较的结果,因为比较适用于呜咽。 : - )

bool lowestPositive(int u, int v, int& result)
{
  result = (u + v - abs(u - v))/2;
  return (bool) result - (u + v + abs(u - v)) / 2;
}

注意:如果(u + v)&gt;失败MAX_INT。至少有一个数字必须为正数才能使返回码正确。同样赞成polythinker的解决方案:)

答案 4 :(得分:3)

以下是使用bit twiddling查找min(x, y)的C语言快速解决方案。它是@Doug Currie's answer的修改版本,灵感来自the answer Find the Minimum Positive Value问题:

bool lowestPositive(int a, int b, int* pout)
{
  /* exclude zero, make a negative number to be larger any positive number */
  unsigned x = (a - 1), y = (b - 1);    
  /* min(x, y) + 1 */
  *pout = y + ((x - y) & -(x < y)) + 1; 
  return *pout > 0;
}

示例:

/** gcc -std=c99 *.c && a */
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdbool.h>

void T(int a, int b) 
{           
  int result = 0;   
  printf("%d %d ", a, b);       
  if (lowestPositive(a, b, &result))    
    printf(": %d\n", result);       
  else              
    printf(" are not positive\n");  
}

int main(int argc, char *argv[])
{
  T(5, 6);
  T(6, 5);
  T(6, -1);
  T(-1, -2);

  T(INT_MIN, INT_MAX);
  T(INT_MIN, INT_MIN);
  T(INT_MAX, INT_MIN);
  T(0, -1);
  T(0, INT_MIN);
  T(-1, 0);
  T(INT_MIN, 0);
  T(INT_MAX, 0);
  T(0, INT_MAX);
  T(0, 0);

  return 0;
}

输出:

5 6 : 5
6 5 : 5
6 -1 : 6
-1 -2  are not positive
-2147483648 2147483647 : 2147483647
-2147483648 -2147483648  are not positive
2147483647 -2147483648 : 2147483647
0 -1  are not positive
0 -2147483648  are not positive
-1 0  are not positive
-2147483648 0  are not positive
2147483647 0 : 2147483647
0 2147483647 : 2147483647
0 0  are not positive

答案 5 :(得分:2)

我建议你将函数重构为更简单的函数。此外,这允许您的编译器更好地实施预期的输入数据。

unsigned int minUnsigned( unsigned int a, unsigned int b )
{
   return ( a < b ) ? a : b;
}

bool lowestPositive( int a, int b, int& result )
{
   if ( a < 0 && b < 0 )  // SO comments refer to the previous version that had || here
   {
       return false;
   }

   result = minUnsigned( (unsigned)a, (unsigned)b );  // negative signed integers become large unsigned values
   return true;
}

这适用于ISO C允许的所有三个有符号整数表示: 二的补码,一个补码,甚至符号/幅度。我们所关心的是任何正有符号整数(MSB已清除)都会将下面的任何内容与MSB集进行比较。

这实际上编译为包含x86的clang的非常好的代码,you can see on the Godbolt Compiler Explorer。 gcc 5.3不幸does a much worse job

答案 6 :(得分:2)

这将根据您的要求处理所有可能的输入。

bool lowestPositive(int a, int b, int& result)
{
    if ( a < 0 and b < 0 )
        return false

    result = std::min<unsigned int>( a, b );
    return true;
}

话虽如此,您提供的签名允许出现偷偷摸摸的错误,因为很容易忽略此函数的返回值,或者甚至不记得有必须检查的返回值以了解结果是否正确的。

您可能更喜欢其中一种替代方案,这使得更难忽视必须检查成功结果:

boost::optional<int> lowestPositive(int a, int b)
{
    boost::optional<int> result;
    if ( a >= 0 or b >= 0 )
        result = std::min<unsigned int>( a, b );
    return result;
}

void lowestPositive(int a, int b, int& result, bool &success)
{
    success = ( a >= 0 or b >= 0 )

    if ( success )
        result = std::min<unsigned int>( a, b );
}

答案 7 :(得分:2)

这里的大量答案都忽略了零不是正面的事实:)

狡猾的演员和燕鸥:

bool leastPositive(int a, int b, int& result) {
    result = ((unsigned) a < (unsigned) b) ? a : b;
    return result > 0;
}

不太可爱:

bool leastPositive(int a, int b, int& result) {
    if(a > 0 && b > 0)
        result = a < b ? a : b;
    else
        result = a > b ? a : b:
    return result > 0;
}

答案 8 :(得分:1)

三元运算符的使用(滥用?)三行

int *smallest_positive(int *u1, int *u2) {
    if (*u1 < 0) return *u2 >= 0 ? u2 : NULL;
    if (*u2 < 0) return u1;
    return *u1 < *u2 ? u1 : u2;
}

如果u1和u2均为阴性,不知道效率或做什么。我选择返回NULL(必须在调用者中检查);返回指向静态-1的指针可能更有用。

编辑以反映原始问题的变化:)

bool smallest_positive(int u1, int u2, int& result) {
    if (u1 < 0) {
        if (u2 < 0) return false; /* result unchanged */
        result = u2;
    } else {
        if (u2 < 0) result = u1;
        else result = u1 < u2 ? u1 : u2;
    }
    return true;
}

答案 9 :(得分:1)

黑客使用“魔法常数”-1:

enum
{
    INVALID_POSITIVE = -1
};

int lowestPositive(int a, int b)
{
    return (a>=0 ? ( b>=0 ? (b > a ? a : b ) : INVALID_POSITIVE ) : INVALID_POSITIVE );
}

这并没有假设数字为正数。

答案 10 :(得分:1)

伪代码,因为我手头没有编译器:

////0 if both negative, 1 if u0 positive, 2 if u1 positive, 3 if both positive
switch((u0 > 0 ? 1 : 0) + (u1 > 0 ? 2 : 0)) {
  case 0:
    return false; //Note that this leaves the result value undef.
  case 1:
    result = u0;
    return true;
  case 2:
    result = u1;
    return true;
  case 3:
    result = (u0 < u1 ? u0 : u1);
    return true;
  default: //undefined and probably impossible condition
    return false;
}

这是紧凑的,没有很多if语句,但依赖于三元“?:”运算符,这只是一个紧凑的if,then,else语句。 “(是吗?”是“:”否“)”返回“是”,“(假?”是“:”否“)返回”否“。

在每个案例之后的正常switch语句中你应该有一个中断;,退出交换机。在这种情况下,我们有一个return语句,所以我们退出整个函数。

答案 11 :(得分:1)

在充分尊重的情况下,您的问题可能是用于描述问题的英语短语确实隐藏了一些复杂性(或至少一些未解决的问题)。根据我的经验,这也是“现实世界”中错误和/或未实现期望的常见来源。以下是我观察到的一些问题:

  • 有些程序员使用命名 领先u的惯例 意味着未签名,但你没有 明确说明你的 “数字”未签名或签名 (或者,就此而言,他们是否 甚至应该是不可或缺的!)

  • 我怀疑我们所有人都读过它 假设一个论点是 那么,积极而另一个则不是 (唯一)正的参数值 是正确的反应,但那是 没有明确说明。

  • 说明也没有定义 两个值都需要的行为 是非正面的。

  • 最后,一些回复 似乎是在这篇文章之前提供的 意味着响应者的想法 (错误地)0是正面的!一个 更具体的要求声明 可能有助于防止任何 误解(或说清楚 没有问题的是零问题 当完全想到的时候 要求是写的。)

我不是要过于挑剔;我只是建议一个更精确编写的要求可能会有所帮助,并且可能也会清楚地表明你在实现中关注的某些复杂性是否真的隐含在问题的本质中。

答案 12 :(得分:1)

uint lowestPos(uint a, uint b) { return (a < b ? a : b); }

您正在寻找最小的正面,仅在这种情况下接受正值是明智的。您不必在函数中捕获负值问题,您应该在调用函数的早期点解决它。出于同样的原因,我离开了布尔值。

一个先决条件是它们不相等,你会以这种方式使用它:

if (a == b)
  cout << "equal";
else
{
  uint lowest = lowestPos(a, b);
  cout << (lowest == a ? "a is lowest" : "b is lowest");
}

如果要更改结果,可以在想要阻止更改或引用时引入const。在正常情况下,计算机将优化甚至内联功能。

答案 13 :(得分:0)

我的想法是基于使用min和max。并将结果分为三种情况,其中

  • min&lt; = 0且max&lt; = 0
  • min&lt; = 0且max&gt; 0
  • min&gt; 0和最大值> 0

最好的是它看起来并不太复杂。 代码:

bool lowestPositive(int a, int b, int& result)
{
    int min = (a < b) ? a : b;
    int max = (a > b) ? a : b;

    bool smin = min > 0;
    bool smax = max > 0;

    if(!smax) return false;

    if(smin) result = min;
    else result = max;

    return true;
}

答案 14 :(得分:0)

在我的第一篇文章遭到拒绝后,请允许我建议您过早地优化问题,并且您不应该担心会有很多if语句。你自然编写的代码需要多个'if'语句,以及它们是用三元if运算符(A?B:C)表示还是经典if块表示,执行时间是相同的,编译器几乎会优化所有的代码都发布在几乎相同的逻辑中。

关注代码的可读性和可靠性,而不是试图智胜未来的自己或其他读取代码的人。我发布的每个解决方案都是O(1),也就是说,每个解决方案都会对代码的性能贡献微不足道。

我想建议这篇文章标记为“过早优化”,海报并不是在寻找优雅的代码。