找到最小正值

时间:2008-12-18 01:54:40

标签: algorithm delphi optimization

从固定数字(在本例中为3)中找到最小非零正值的最佳算法是什么,如果没有正问题则返回0?

我的天真方法如下(在Delphi中,但随意使用你喜欢的任何东西),但我认为这是一种更优雅的方式。

value1Temp := MaxInt;
value2Temp := MaxInt;
value3Temp := MaxInt;

if ( value1T > 0) then
  value1Temp := value1;
if ( value2 > 0) then
  value2Temp := value2;
if ( value3 > 0) then
  value3Temp  := value3;

Result := Min(value1Temp, Min(value2Temp, value3Temp));
if Result = MaxInt then
  Result := 0;

编辑抱歉,如果没有正数,则添加了所需内容。我以为我以前曾经在那里,但一定是错过了。

21 个答案:

答案 0 :(得分:3)

我会做一个小循环(这是在C中,我不是德尔福人):

int maxPositiveValue(int *number, int listSize)
{
    int i, result = 0;

    for(i = 0; i < listSize; i++)
    {
        if(number[i] > 0 && (number[i] < result || result == 0))
            result = number[i];
    }

    return result;
}

此代码的优点是它非常易读,可以轻松扩展以处理任何长度的值列表。

更新:我已更改代码以回应我在下面收到的评论。

这个新代码稍微复杂一些,现在它将处理:

  1. 列表中不包含正整数的情况(返回0)。
  2. 列表包含一个或多个INT_MAX出现的情况。
  3. 任何长度的列表。

答案 1 :(得分:2)

在DELPHI中 - 如果您的域名是整数,并且您可以将args放入longins中,并且如果您可以避免传递最小整数($ 80000000),那么这将为您提供所需的结果而无需任何条件分支:

function cmMinInt( XX, YY, ZZ : longint ) : longint;
begin
   result := max(0,longint(
   min(longint((XX-1) xor $80000000),
   min(longint((YY-1) xor $80000000),
   longint((ZZ-1) xor $80000000)
   )) xor $80000000)+1);
end;

该技术依赖于longint类型的可逆无损重新映射,因此我们感兴趣的范围 - 从1到MAXINT的整数 - 保持有序并占据最低值。简单地切换符号位几乎可以提供我们需要的东西,除了我们不希望0包含在较低范围内。首先减去1(并稍后再添加)修复了这个问题。这里使用的xor操作将两个操作数扩展为int64,这需要显式强制转换为longint,因此min函数将生成正确的结果。最后,如果操作数都是neg,则最小值将在上限范围内找到,答案将为neg。在这种情况下,我们希望答案为0,因此我们只需使用max函数剪辑它。

这是相同的数学分布在多个语句上以便于阅读:

function cmMinInt( XX, YY, ZZ : longint ) : longint;
begin
   // swap ordinal coding for range MININT..0 with range 1..MAXINT
   XX := XX-1;             // move region division to between 0 and 1
   XX := XX xor $80000000; // swap regions, preserving ordering
   XX := longint(XX);      // cram back into signed 32-bit
   // similarly with YY and ZZ
   YY := longint((YY-1) xor $80000000);
   ZZ := longint((ZZ-1) xor $80000000);
   // find min of three recoded values
   result := min(XX,min(YY,ZZ));
   // swap ordering back again
   result := result xor $80000000;  // swap regions, preserving ordering
   result := result+1;              // move region division back home
   result := longint(result);       // cram back into signed 32-bit
   // if all three were neg, result at this point is neg -- clip to zero
   result := max(0,result);
end;

-Al。

答案 2 :(得分:2)

我不知道Delphi,但这里是Ruby的快速解决方案(假设数字在列表中)

[1,2,42,-12].delete_if{|n| n <= 0 }.min || 0

在算法上,删除所有负(或0)元素,然后找到最小值。如果没有正面元素,[].min会返回nil,因此最终|| 0会将请求的“0”作为答案。

答案 3 :(得分:2)

以下函数如何(当然在Delphi中):

function LowestPositiveInt(IntArr : array of Integer):Integer;
var
  iX : integer;
  bValid : boolean;
begin
  Result := MaxInt;
  bValid := False;
  for ix := 0 to High(IntArr) do
    if (IntArr[ix] > 0) then
      begin
        bValid := true;
        if (IntArr[iX] < Result) then
          Result := IntArr[ix];
      end;
  if not bValid then
    Result := 0;
end;

然后将其称为如下:

ShowMessage(IntToStr( LowestPositiveInt([5,2,3,-1,12]) ));

这应该返回2.这种方法的优点是数组可以包含任意数量的项目,包括整数变量......所以使用上面的例子你可以说:

Result := LowestPositiveInt( [ Value1, Value2, Value3 ] );

编辑已更新以处理 LowestPosititiveInt([MaxInt,MaxInt,MaxInt])方案。

答案 4 :(得分:2)

我会这样做:

  

结果:= MaxInt;
  如果value1&gt; 0然后结果:= min(结果,值1);
  如果value2&gt; 0然后结果:= min(结果,值2);
  如果value3&gt; 0然后结果:= min(结果,值3);
  如果Result = MaxInt,则结果:= 0;

如果你想在一个有任意数量问题的循环中,那么:

  

结果:= MaxInt;
  对于我:= 1到N做
    如果值[I]> 0然后结果:= min(结果,值[I]);
  如果Result = MaxInt,则结果:= 0;

如果您希望value数组从零开始,请将for循环更改为:0到N-1

我认为这段代码非常清楚地知道正在做什么。

将“then”语句放在同一行上会使代码在这种简单的情况下看起来更干净,但如果您认为有必要,可以随意将“then”语句缩进到下一行。

答案 5 :(得分:1)

我同意亚当。如果你只需要容器中最小的自然数,那么你不会比算法上的线性搜索更快。

他的代码运行速度非常快,很可能会转换为x86中的CMOV,因此for循环中的if语句不会花费那么多。

如果您最终想要按顺序排列所有非零数字,那么当然排序,然后拼接会好得多。

答案 6 :(得分:1)

你在寻找美学还是速度?

如果是后者,我想不出一种方法可以在应用程序中检测到足够多次执行此测试:它无关紧要。

干杯

答案 7 :(得分:1)

如果使用非固定数量的值,您想要的是selection algorithm

但是,如果您的代码只需要检查三个值,则应该避免循环和特定算法,并且只关注微优化 - 特别是尽可能少的分支。

Hacker's Delight第4章中有一些相关内容,你可以 将您签名的整数强制转换为无符号,以便将分支数量减半。这是 在下面的C代码中的函数smallest_v2()中完成:

#include <stdio.h>
#include <limits.h>

int smallest_v1(int a, int b, int c)
{
        int min = INT_MAX;

        min = a>0 && a<min ? a : min;
        min = b>0 && b<min ? b : min;
        min = c>0 && c<min ? c : min;
}

// See Hacker's Delight, chapter 4.
int smallest_v2(int a, int b, int c)
{
        int min = INT_MAX;

        if ( (unsigned) a < min ) min = a;
        if ( (unsigned) b < min ) min = b;
        if ( (unsigned) c < min ) min = c;

        return min;
}

int main()
{
        printf("min v1: %d\n", smallest_v1(-10, 7, 3));
        printf("min v2: %d\n", smallest_v1(-10, 7, 3));
}

基本上,这本书说如果你想检查是否

1 <= i <= 10

然后这与进行无符号比较相同

(unsigned)(i - 1) <= 9

这本书也提供了证据。你得到的是你的代码中更好的分支预测。你应该制作一个测试程序并计时。

答案 8 :(得分:0)

这是我在想到它之后想出来的

Result := 0;
if value1 > 0 then
  Result := value1;
if (value2 > 0) and ((Result = 0) or (value2 < Result)) then
  Result := value2;
if (value3 > 0) and ((Result = 0) or (value3 < Result)) then
  Result := value3;

如果你有一个列表,那么更通用的算法会更好。

答案 9 :(得分:0)

涵盖所有基础的c#版本(我认为):

public int GetMinimumPositiveValue(IEnumerable<int> values)
{
    int result = int.MaxValue;
    bool hasPositiveValue = false;

    foreach (int value in values)
    {
        if(value == 1) { return value; } 

        if(value > 0)
        {
            hasPositiveValue = true;

            if(value < result)
            {
                result = value;
            }
        }
    }

    return hasPositiveValue ? result : 0;
}

答案 10 :(得分:0)

这是在问题帖子中解决问题的C版本,但修复了所有值都是MaxInt的情况......

int foo(int value1, int value2, int value3)
{
    int value1Temp, value2Temp, value3Temp, tempMax;
    value1Temp = max(value1, 0);
    value2Temp = max(value2, 0);
    value3Temp = max(value3, 0);
    tempMax = value1Temp | value2Temp | value3Temp;
    if (value1Temp == 0) { value1Temp = tempMax; }
    if (value2Temp == 0) { value2Temp = tempMax; }
    if (value3Temp == 0) { value3Temp = tempMax; }
    return min(value1Temp, min(value2Temp, value3Temp));
}

也可以以无分支的方式执行此操作,因为min和max可以实现为无分支操作:

int min(int x, int y)
{
    return y + ((x - y) & -(x < y));
}

int max(int x, int y)
{
    return x - ((x - y) & -(x < y));
}

int foo(int value1, int value2, int value3)
{
    int value1Temp, value2Temp, value3Temp, tempMax, mask;
    value1Temp = max(value1, 0);
    value2Temp = max(value2, 0);
    value3Temp = max(value3, 0);
    tempMax = value1Temp | value2Temp | value3Temp;
    mask = -(value1Temp > 0);
    value1Temp = (value1Temp & mask) | (tempMax & ~mask);
    mask = -(value2Temp > 0);
    value2Temp = (value2Temp & mask) | (tempMax & ~mask);
    mask = -(value3Temp > 0);
    value3Temp = (value3Temp & mask) | (tempMax & ~mask);
    return min(value1Temp, min(value2Temp, value3Temp));
}

有关您为什么要这样做的更多背景信息,请参阅:Is "If" Expensive?Bit Twiddling Hacks

编辑:我早先尝试过无分支解决方案,这实际上并没有起作用。添加了一个新的无分支解决方案,该解决方案应该可以使用。

答案 11 :(得分:0)

Jason关于正确处理仅包含负值的空集合和集合的建议略有改进:

values.Min(r => r > 0 ? r : (int?)null) ?? 0

答案 12 :(得分:0)

//return the smallest non-zero positive number, or null.
//relying on Min or Max is considered cheating.
public static int? smallestNonZeroPositiveNumberOfThree(
    int val1, int val2, int val3)
{
    //we have no guarantee that any of the 3 inputs will be positive
    int? result = null;
    if (val1 > 0) 
    {
        result = val1; 
        if (val2 > 0 && val2 < result) { result = val2; }
        if (val3 > 0 && val3 < result) { result = val3; }
    }
    else if (val2 > 0)
    {
        result = val2; 
        if (val3 > 0 && val3 < result) { result = val3; }
    }
    else if (val3 > 0)
    {
        result = val3; 
    }
    return result;
}

答案 13 :(得分:0)

这是C#

public int GetSmallestPositive(IList<int> pValues)
{
    if(pValues == null)
    {
        throw new ArgumentNullException("pValues");
    }
    int _negNumCount = 0;
    int _smallest = int.MaxValue;
    for(int i = 0; i < pValues.Count; ++i)
    {
        if(pValues[i] < _smallest)
        {
            if(pValues[i] <= 0)
            {
                ++_negNumCount;
                continue;
            }
            _smallest = pValues[i];
            if(_smallest == 1)
            {
                return 1;
            }
        }
    }
    return (_negNumCount == pValues.Count) ? 0 : _smallest;
}

在这种情况下,在您的示例中,我使用的是int,因此1是最小的非零数字。只要您将所有内容放入列表中,它就可以使用任意数量的值。

编辑:已修复如果列表中有负数,则返回0。如果pValues为null,则抛出异常。

答案 14 :(得分:0)

无论数组元素的类型是什么

,这都有效
template <typename T>
T min_pos(T* a, int n)
{
    int i /* = 0 */ /* Edit: commented this out */;

    // Find the first positive element in the array
    for (i = 0; i < n; ++i)
        if (a[i] > 0)
            break;

    // If no positive element was found, return 0
    if (i == n)
        return 0;

    T res = a[i];

    // Search the rest of the array for an element
    // that is positive yet smaller than res
    for (++i; i < n; ++i)
    {
        if ((a[i] > 0) && (a[i] < res))
            res = a[i];
    }

    return res;
}

答案 15 :(得分:0)

Result := Min(IfThen(Value1 > 0, Value1, MAXINT), 
              Min(IfThen(Value2 > 0, Value2, MAXINT),
                  IfThen(Value3 > 0, Value3, MAXINT)));

如果输入不是列表/数组,则循环将不起作用。

如果这三个中没有一个是正数且非零,那么问题不清楚该函数应该做什么。

答案 16 :(得分:0)

在Haskell中,像往常一样,最容易解决一般问题,然后宣布一个特例。

foo xs = let xs1 = filter (>0) xs in if null xs1 then 0 else minimum xs1

foo3 x1 x2 x3 = foo [x1, x2, x3]

答案 17 :(得分:0)

很难相信Delphi仍然限制在Min函数中的两个值。 : - (

在Python中,您可以设置一个函数来处理任意数量的参数,如下所示:

def PosMin(*numbers):
    try:
        return min(num for num in numbers if num > 0)
    except:
        return 0

不幸的是,如果“min”函数最终没有值(例如空列表或在这种情况下没有数字> 0),则会引发异常,因此必须捕获异常。有一个建议下一个版本的python添加一个标记值,看起来它将被接受。在这种情况下,代码就是

return min (num for num in numbers if num > 0, sentinel=0)

,无需进行异常处理。

答案 18 :(得分:0)

function MinPositive(const AIntegers: array of Integer): Integer;
var
  LoopI: Integer;
  LFirstPositivePos: Integer;
begin
  Result := 0;
  LFirstPositivePos := MaxInt;
  for LoopI := Low(AIntegers) to High(AIntegers) do
  begin
    if (AIntegers[LoopI] > 0) then
    begin
      Result := AIntegers[LoopI];
      LFirstPositivePos := LoopI;
      Break;
    end;
  end;

  for LoopI := LFirstPositivePos to High(AIntegers) do
  begin
    if (AIntegers[LoopI] > 0) and (AIntegers[LoopI] < Result) then
    begin
      Result := AIntegers[LoopI];
    end;
  end;
end;

function MinPositive3(const I1, I2, I3: Integer): Integer;
begin
  Result := MinPositive([I1, I2, I3]);
end;

答案 19 :(得分:-1)

对于那些试图解决C#中的一般问题的人来说,我看到的代码行太多了。如果值为IEnumerable<int>

values.Select(v => (int?)v)
      .Where(v => v > 0)
      .Min() ?? 0;

返回values中最小的正值(如果存在),否则返回0。在这里,我正在利用Enumerable.Min(IEnumerable<int?>)如果序列为空将返回null的事实。因此,我们过滤掉非正值,然后找出最小值。如果所有值都是非正数,则我们根据需要获得零,否则找到正值的最小值。

答案 20 :(得分:-1)

  • 浏览列表的值,丢弃它们,直到找到正值,将min值设置为
  • 浏览列表其余部分的值
    • 如果0 <当前值&lt; min-value,将min-value设置为current-value
  • return min-value