从固定数字(在本例中为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;
编辑抱歉,如果没有正数,则添加了所需内容。我以为我以前曾经在那里,但一定是错过了。
答案 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 :(得分: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)