在 C++ 中获取中间值
Posted
技术标签:
【中文标题】在 C++ 中获取中间值【英文标题】:Get mid value in C++ 【发布时间】:2014-06-20 17:46:32 【问题描述】:考虑三个值x, y, z
。
得到中间值的公式是什么(不是平均值,而是既不是min
也不是max
的值)?
const double min = std::min(x, std::min(y, z));
const double mid = /* what formula here ? */
const double max = std::max(x, std::max(y, z));
【问题讨论】:
也许没有一个简洁的对称公式? @AbhishekBansal 如果他们都是平等的怎么办?如果只有两个相等怎么办? 仅供参考,这称为中位数。 Related. 随便选一个。然后,您的应用将运行 1/3 的时间,这比我当前的应用所达到的要多得多。 顺便说一句,在 C++11 中,min
可以重写为std::min(x, y, z)
。
【参考方案1】:
这看起来像是作弊,但是:x + y + z - min - max
【讨论】:
我喜欢。唯一的问题是溢出。 @keyser:是的,所以也值得发布一个不同的答案,以防万一。此外,这不一定是返回变量,只是返回值。 @Laszlo True(尽管可以说这是最小值和最大值的错误特征)。因为我们没有参考答案,所以没关系。 您可以在没有混合和最大的情况下获得 med,因此它们的限制不一定相关。据我所知,至少有两种选择或不使用最小值和最大值。【参考方案2】:比艾伦的伎俩丑一点,但不会造成溢出,也不会造成数值错误等等:
int x, y, z, median;
...
if (x <= y && y <= z || y >= z && y <= x) median = y;
else if (y <= x && x <= z || x >= z && x <= y) median = x;
else median = z;
算法很简单:
检查x是否在y和z之间,如果是,就是这样。
检查y是否在x和z之间,如果是,就是这样。
它必须是 z,因为它既不是 x 也不是 y。
================================================ ======
如果你有三个以上的元素,你也可以更灵活地得到这个。
//或异或实现,无所谓……
void myswap(int* a, int* b) 国际温度 = * b; *b = *a; *a = 温度;
int x, y, z;
// Initialize them
int min = x;
int med = y;
int max = z;
// you could also use std::swap here if it does not have to be C compatible
// In that case, you could just pass the variables without the address operator.
if (min > med) myswap(&min, &med);
if (med > max) myswap(&med, &max);
if (min > med) myswap(&min, &med);
【讨论】:
如果您想要所有三个(最小、中间、最大),最好只对列表进行排序。一次比较并交换以使前两个按顺序排列,然后最多进行两次测试以计算出另外两个的顺序。 是的,虽然这将是一个特定的解决方案,而且这个解决方案也可以在 C 中工作,这与 std::swap 不同。 :P True - 虽然寻找中位数(或 nth_greatest)的一般算法是基于快速排序,但跳过了不必要的工作。 @AlanStokes:当然,我同意。该版本针对可读性和综合性进行了优化,而该版本针对效率和灵活性进行了优化。这里没有效率问题,但是 OP 似乎在询问三个值,所以我认为两者都是有效的。但是,是的,排序也适用于 3 个以上的元素;只需要确定奇数。 @AlanStokes:更新了与 C 兼容的交换排序版本(即冒泡排序),以防万一。 :D【参考方案3】:以对称的方式一次找到所有三个:
min = x; med = y; max = z;
if (min > med) std::swap(min, med);
if (med > max) std::swap(med, max);
if (min > med) std::swap(min, med);
【讨论】:
这不是冒泡排序吗? @AbhishekBansal 这确实是冒泡排序。 @OliCharlesworth:我知道是对的。我不想问这个:那又怎样?我不能说它在 C 中不起作用?你认为“只是说”是什么意思? ;) @LaszloPapp:只是不确定您之前评论的目的是什么,仅此而已(此代码在 Java 中也不起作用!)。 (例如,这很容易被视为对标签的误读。) @OliCharlesworth:“只是说”通常意味着这不是什么大问题,而且这个人知道情况。在谈论兼容性时,在 C++ 上下文中将 Java 与 C 进行比较很奇怪,我必须这么说,特别是考虑到我的回答在一小时前就表明了这一点。 Fwiw,无论我有其他答案,我什至都对答案(以及问题)投了赞成票。【参考方案4】:Alan 的“作弊”的一种变体,(有时)防止溢出:
#include <iostream>
#include <algorithm>
using namespace std;
int main(int argc, char *argv[])
double a = 1e308;
double b = 6e306;
double c = 7.5e18;
double mn = min(a,min(b,c));
double mx = max(a,max(b,c));
double avg = mn + (mx-mn)*0.5;
double mid = a - avg + b - avg + c;
cout << mid << endl;
输出:
6e+306
它利用了二分查找中常用的avg-formula来防止溢出:
两个值的平均值可以计算为low + (high-low)/2
但是,它仅适用于正值。可能的后备方案包括 Alan 的回答,或者只是 (x+y)/2
进行平均计算。
请注意,双精度在这里发挥作用,可能会导致mid
-计算出现问题。不过,它对正整数非常有效:)
【讨论】:
中间变量访问不到的问题还在。 @LaszloPapp 这不是问题的一部分。这是关于寻找价值 不,不是,但读者可以意识到算法的局限性。 :) 这不是限制,而是规范:p 在我看来,找到变量是一个完全独立的问题。我的意思是,将其称为问题是错误的。 请注意,很多人都在使用 Stack Overflow,他们可能会将此处的算法用于一般情况。是的,你可以说,“这是他们的问题”,如果他们误会了,但恕我直言,防止这种情况更好。当然,这并不是说你的答案不可用等等。【参考方案5】:来自this link的答案分享在cmets:
const double mid = std::max(std::min(x,y),std::min(std::max(x,y),z));
编辑 - 正如 Alan 所指出的,我错过了一个案例。我现在给出了一个更直观的证明。
直接证明:不失关于 x 和 y 的一般性。
从最里面的表达式开始,min(max(x,y),z)
...
-
如果它返回 z,我们找到了关系:max(x,y) > z。然后表达式计算为
max(min(x,y),z)
。通过这个我们可以确定 min(x,y) 和 z 之间的关系。如果 min(x,y) > z,那么 z 小于 x 和 y(如关系变为:max(x,y) > min(x,y) > z)。因此 min(x,y) 确实是中位数,max(min(x,y),z)
会返回它。如果 min(x,y) ,那么 z 确实是中位数(如 min(x,y) ).
如果它返回 x,那么我们有 x 和 y 。表达式的计算结果为:max(min(x,y),x)
。由于 max(x,y) 计算为 x,min(x,y) 计算为 y。获取关系 z > x > y。我们返回 x 和 y 的最大值(因为表达式变为 max(y,x)
),它是 x 也是中值。 (请注意,y 的证明是对称的)
证明结束
旧证明 - 注意它不完整(直接):
不失一般性: 假设 x > y > z x 和 y 的最小值是 y。并且(x 和 y 的最大值)和 z 的最小值是 z。 y 和 z 的最大值为 y,即中位数。
假设 x = y > z x 和 y 的最小值就是 x。并且(x 和 y 的最大值是 x)和 z 的最小值是 z。 上面两个的最大值是x,也就是中位数。
假设 x > y = z x 和 y 的最小值是 y。并且(x 和 y 的最大值是 x)和 z 的最小值是 z。 上面两个的最大值是y,也就是中位数。
最后,假设 x = y = z 这三个数字中的任何一个都是中位数。使用的公式将返回一些数字。
【讨论】:
是的,它有效。但是,它的可读性不高,因此难以维护;乍一看还不清楚发生了什么。相比之下,使用我的解决方案“auto mid = median(x,y,z)”,可以立即识别出该函数返回三个值的中值。 我并不完全相信你的证明。 x @AlanStokes 不错。我会调查它,并改进答案。 (如果可能,可能尝试给出更优雅的证明)。【参考方案6】:最好的方法是使用通用中值函数模板。无需复制、交换或数学运算。
template <typename T>
const T& median(const T& a, const T& b, const T& c)
if (a < b)
if (b < c)
return b;
else if (a < c)
return c;
else
return a;
else if (a < c)
return a;
else if (b < c)
return c;
else
return b;
【讨论】:
“最好的方法”实际上对于非性能关键代码的 if/elseif/else 丛林是不可读的。 :) 我认为在这种情况下全面比微优化更好。此外,当您需要计算奇数数量的中位数时,这不会超过三个元素.. imo 它不是不可读的;它很优雅。您可能只是看了几秒钟,然后立即宣布它不可读,而没有对其进行足够的思考。此外,他只寻找三个值的中位数。 我之前实际上在考虑模板,但后来我决定不提它们,因为恕我直言,它比需要的复杂。此外,我没有看到任何不可忽略的性能提升。它基本上是我的第一个解决方案的模板化版本,具有不同的 if/elseif/else 排序,但与我的非模板代码相比,我不确定这里的模板增益。对我来说,这似乎使它变得比必要的复杂。我同意的一件事是,如果您要处理不同的类型,它会很有用,因此在这个意义上它是灵活的。以上是关于在 C++ 中获取中间值的主要内容,如果未能解决你的问题,请参考以下文章