【问题标题】:Neatest / Fastest Algorithm for Smallest Positive Number最小正数的最佳/最快算法
【发布时间】:2008-12-26 15:26:51
【问题描述】:

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

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

谢谢, 丹

这是一个简单的例子:

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


lowestPositive(5, 6, result);

【问题讨论】:

  • 对不起,如果两者都是否定的,答案是什么?
  • 返回假!即-没有答案,我将编辑问题以提供示例...
  • 结果应该是什么? 0?不变?两者的绝对值最低?

标签: c++ algorithm optimization


【解决方案1】:

如果值用二进制补码表示,那么

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

将起作用,因为二进制补码中的负值在被视为无符号时大于正值。与 Jeff 的回答一样,这假设至少有一个值是正数。

return result >= 0;

【讨论】:

  • 聪明。有点笨拙,但仍然很聪明。
  • @tvanfosson:如果两个参数都为负,则结果为负,函数返回 false,因此在这种情况下,result 的具体值无关紧要。
  • @Shmoopty:你能提供一个函数给出错误答案的例子吗?
  • @JF -- 我现在明白了。仍然我能够错过的事实使它成为我的“神奇”解决方案。我倾向于避免聪明,并采用更易于阅读的解决方案。在这种情况下,答案的实际文本让我失望。另一个教训:阅读代码,而不是文档! :-)
  • 尚不清楚 OP 是否考虑正数或非负数,因此我发布了正数的答案,即不包括零。 stackoverflow.com/questions/393885/…
【解决方案2】:

比起简洁,我更喜欢清晰:

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
}

【讨论】:

  • 酷。我也更喜欢这种清晰度:)
  • 并且对输入的符号没有限制。 :)
  • 几乎完美,在赋值后立即返回true或false可能总是更快(gcc实际上像现在一样再次测试结果,即使在return语句时已经知道结果是否为正已到达)
  • 正如@HugoHeden 的回答指出的那样,如果a &gt; 0b &lt; 0 则失败。他的回答基本上是这个模板,以及第一个if()中的一个额外条件。
  • @HugoHeden 不错,抓到 - 已修复。
【解决方案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) > max_int 则失败。至少一个数字必须是正数,返回码才是正确的。也感谢 polythinker 的解决方案:)

【讨论】:

  • 是的,它叫做数学。它的目的是开玩笑地阅读。 :)
  • @dmckee:也许这读起来更好:“midpoint=(u+v)/2;dist=abs(u-v)/2; result = midpoint-dist;” --这表明结果总是得到两个值中较小的一个(不管符号是什么),但是“返回(布尔)结果-(中点+距离);”返回 true 除非 u=v: 所以这是错误的:P
  • @ShreevatsaR:我必须承认,我没有测试代码。很棒的分析! ;) 不正确的返回值归结为我的想法错误。 return result > 0 是正确的,但不幸的是其中有一个古怪的比较。
  • 别误会我。我印象深刻。故意使用错误的工具很酷,但前提是它真的很尴尬。这个符合条件。
  • 除了公认的答案,我发现这是最容易理解的:)
【解决方案4】:
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));

编辑:认为这不是那么有趣,所以我删除了它。但是再想一想,把它留在这里是为了好玩:)这可以被认为是道格答案的转储版本:)

【讨论】:

  • 这段代码被修改是荒谬的。它做了它应该做的事情,我可能会很聪明地补充一下。所以我投票 +1。
  • 这个话题很有趣。公主,为什么我们必须总是那么认真? :) 嘿,这是聪明的代码。为什么要浪费在-1涅槃?
  • 为了处理不是 32 位宽的整数,像 (1 ::digits) 这样的掩码或没有模板 ((1
  • 对。它并不是为了应对不同的尺寸。另一种方法是 (unsigned int) -1 ^ ( (unsigned int) -1 >> 1 ),它应该适用于几乎所有 C 编译器。
【解决方案5】:

这是使用bit twiddling 查找min(x, y) 的C 语言快速解决方案。它是@Doug Currie's answer 的修改版本,灵感来自the answerFind 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

【讨论】:

    【解决方案6】:

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

    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】:

      这里的很多答案都忽略了零不是正数的事实:)

      具有棘手的铸造和燕鸥:

      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】:

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

        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

        【讨论】:

        • 返回最高的正数。
        • a = -1 b = 1 应该返回 1 但不会
        • 根据问题集,我正在处理 > 0 的整数集,因为我的 if 语句强制执行该操作,并且因为这是 OP 所要求的。此外,我试图证明的是,通过将函数重构为更小的部分,逻辑变得更加简单。
        • 你的第一个 if 语句的感觉是不正确的。如果 a 和 b 都为正则返回 false,如果 a 和 b 为负则返回 false。
        • 有趣地使用 unsigned 将负数转换为高于最大有符号 int 的数字。不过,这需要评论!
        【解决方案9】:

        使用“魔法常数”-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】:

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

          ////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 语句。 "(true ? "yes" : "no")" 返回 "yes","(false ? "yes" : "no") 返回 "no"。

          在每个 case 之后的正常 switch 语句中,你应该有一个 break;,退出 switch。在这种情况下,我们有一个 return 语句,所以我们要退出整个函数。

          【讨论】:

          • -1:抱歉,但这不是我希望在生产中看到的代码类型。有更简洁的方法可以获得 OP 想要的结果,并且您的 switch 语句上方的注释表明您知道代码是如此“聪明”以至于它不再可读。
          【解决方案11】:

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

          • 一些程序员使用命名 其中一个领先的公约u 暗示未签名,但你没有 明确说明您的 “数字”是无符号或有符号的 (或者,就此而言,他们是否 甚至应该是不可分割的!)

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

          • 描述也没有定义 如果两个值都需要的行为 是非阳性的。

          • 最后,一些回应 在这篇文章之前提供的似乎 暗示响应者认为 (错误地)0 是正数!一种 更具体的要求说明 可能有助于防止任何 误解(或说清楚 零的问题不是 当 要求已写)。

          我并不想过分挑剔;我只是建议写一个更精确的需求可能会有所帮助,并且可能还会清楚您在实现中关注的某些复杂性是否真的隐含在问题的本质中。

          【讨论】:

            【解决方案12】:

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

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

            【讨论】:

              【解决方案13】:
              uint lowestPos(uint a, uint b) { return (a < b ? a : b); }
              

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

              前提是它们不相等,你可以这样使用它:

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

              如果要更改结果,则可以在要防止更改或引用时引入 const。正常情况下电脑会优化甚至内联函数。

              【讨论】:

                【解决方案14】:

                没有聪明,合理的清晰度,适用于整数和浮点数:

                template<class T>
                  inline
                  bool LowestPositive( const T a, const T b, T* result ) {
                  const bool b_is_pos = b > 0;
                  if( a > 0 && ( !b_is_pos || a < b ) ) { 
                    *result = a;
                    return true;
                  }
                  if( b_is_pos ) {
                    *result = b; 
                    return true;
                  }
                  return false;
                }
                
                • 请注意,0(零)不是正数。
                • OP 要求处理数字(我将其解释为整数浮点数)。
                • 只有在有肯定结果时才取消引用结果指针(性能)
                • 只测试 a 和 b 的肯定性一次(性能 -- 不确定这样的测试是否昂贵?)

                还要注意accepted answer(由 tvanfosson 提供)是错误的。如果 a 为正而 b 为负(即“都不是正的”),则失败。 (这是我添加单独答案的唯一原因——我没有足够的声誉来添加 cmets。)

                【讨论】:

                  【解决方案15】:

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

                  • min
                  • min 0
                  • min > 0 和 max > 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;
                  }
                  

                  【讨论】:

                    【解决方案16】:

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

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

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2020-11-24
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2013-01-17
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多