在 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++ 中获取中间值的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中获取边界框(矩形)内的像素值和坐标?有啥方法吗?

如何使用从 C++ 发送的地址在 python 中获取值?

在 C++ 中,如何获取存储类实例字段值的地址?

如何在 QT C++ 中从表的列中获取 SQL 中的所有值

从 C++ 中的 PHP 扩展获取函数参数值

在 C++ 中的区域语言设置下获取当前位置值