【问题标题】:Median Maintenance Algorithm - Same implementation yields different results depending on Int32 or Int64中值维护算法 - 相同的实现会根据 Int32 或 Int64 产生不同的结果
【发布时间】:2015-03-09 18:34:48
【问题描述】:

我在做硬件问题时发现了一些有趣的东西。

howework 问题要求对中值维护算法进行编码。

正式声明如下:

这个问题的目标是实现“中值维护”算法(在第 5 周的堆应用讲座中介绍)。文本文件包含从 1 到 10000 的整数列表,未排序;您应该将其视为一串数字,一个接一个到达。令 xi 表示文件的第 ith 个编号,则 kth 中位数 mk 定义为数字 x1,…,xk 的中位数。 (所以,如果 k 是奇数,则 mk 是 ((k+1)/2)th x1 中最小的数,... ,xk;如果k是偶数,则m1是x1th个最小的数/sub>,…,xk.)

为了获得O(n)的运行时间,这显然应该使用堆来实现。无论如何,我使用蛮力进行了编码(截止日期太快,需要立即得到答案)(O(n2)),步骤如下:

  1. 读取
  2. 中的数据
  3. 排序数组
  4. 求中位数
  5. 将其添加到运行时间

我通过几个测试用例(具有已知答案)运行该算法并得到正确的结果,但是当我在更大的数据集上运行相同的算法时,我得到了错误的答案。我正在使用 Int64 ro 进行所有操作来表示数据。 然后我尝试切换到 Int32,神奇地我得到了正确的答案,这对我来说毫无意义。

代码在下面,也找到了here(数据在repo里)。算法在 3810 索引后开始给出错误结果:

    private static void Main(string[] args)
    {
        MedianMaintenance("Question2.txt");
    }

    private static void MedianMaintenance(string filename)
    {
        var txtData = File.ReadLines(filename).ToArray();
        var inputData32 = new List<Int32>();
        var medians32 = new List<Int32>();
        var sums32 = new List<Int32>();
        var inputData64 = new List<Int64>();
        var medians64 = new List<Int64>();
        var sums64 = new List<Int64>();
        var sum = 0;
        var sum64 = 0f;
        var i = 0;
        foreach (var s in txtData)
        {
            //Add to sorted list
            var intToAdd = Convert.ToInt32(s);

            inputData32.Add(intToAdd);
            inputData64.Add(Convert.ToInt64(s));

            //Compute sum
            var count = inputData32.Count;
            inputData32.Sort();
            inputData64.Sort();
            var index = 0;

            if (count%2 == 0)
            {
                //Even number of elements
                index = count/2 - 1;
            }
            else
            {
                //Number is odd
                index = ((count + 1)/2) - 1;
            }
            var val32 = Convert.ToInt32(inputData32[index]);
            var val64 = Convert.ToInt64(inputData64[index]);
            if (i > 3810)
            {
                var t = sum;
                var t1 = sum + val32;
            }
            medians32.Add(val32);
            medians64.Add(val64);
            //Debug.WriteLine("Median is {0}", val);
            sum += val32;
            sums32.Add(Convert.ToInt32(sum));
            sum64 += val64;
            sums64.Add(Convert.ToInt64(sum64));
            i++;
        }
        Console.WriteLine("Median Maintenance result is {0}", (sum).ToString("N"));
        Console.WriteLine("Median Maintenance result is {0}", (medians32.Sum()).ToString("N"));

        Console.WriteLine("Median Maintenance result is {0} - Int64", (sum64).ToString("N"));
        Console.WriteLine("Median Maintenance result is {0} - Int64", (medians64.Sum()).ToString("N"));
    }

更有趣的是,运行总和(在 sum64 变量中)产生的结果与使用 LINQ 的 Sum() 函数对列表中的所有项目求和产生的结果不同。

结果(第三个是错误的):

这些是计算机详细信息:

如果有人能给我一些关于为什么会发生这种情况的见解,我将不胜感激。

谢谢,

【问题讨论】:

  • 尽管有这个名字,Convert 类并不是在已知类型之间转换数据的好方法。您是否从Convert.ToInt32Convert.ToInt64 得到不同的结果?如果您改用Int32.TryParseInt64.TryParse,您会遇到任何故障吗?那么有什么不同吗?
  • 0f 正在初始化一个 32 位浮点变量,您的意思是 0d 或 0.0 接收 64 位浮点数。
  • @BenVoigt Convert.ToInt32/64 的结果产生相同的数字,总和是不同的。而且我还没有尝试使用 TryParse 进行转换。
  • @jtimperley:他们根本不需要浮点数。所有的数字都是整数,所以他们应该保持整数。他们可能想要0L,或者,只使用显式类型而不是var
  • 对于 linq,如果你使用强类型列表,你可能会得到更好的结果。新列表 和新列表。对于最后一条评论,他们不应该需要这么多,但我没有做他们的功课,只是指出他们的错误。

标签: c# algorithm integer int64 int32


【解决方案1】:

0f 正在初始化一个 32 位浮点变量,你的意思是 0d 或 0.0 接收一个 64 位浮点数。

对于 linq,如果您使用强类型列表,您可能会得到更好的结果。

new List<int>()
new List<long>()

【讨论】:

  • 很确定他们想要0L 或只是int64 sum64 = 0。他们正在使用类型列表。
  • @matt-burland 他们有一个浮点数,我给了一个浮点数......仍然没有做他们的功课。
  • @matt-burland 不正确是相对的,任何一种数据类型都会产生相同的结果。我的回答是故意不可知论者的意图。用某人的解决方案解决问题与编写更好的实现之间存在巨大差异。
  • 我很难描述包括“编写更好的实现”这样的小细节。如果您坚持简单地指出问题而不帮助“家庭作业”,那为什么要建议替代数据类型呢?我同意这有点误导
【解决方案2】:

我注意到评论者所做的第一件事:var sum64 = 0f 将 sum64 初始化为浮点数。由于 Int64 集合的中值本身就是 Int64(指定的规则不使用偶基数集合中两个中点值之间的平均值),因此您应该将此变量显式声明为 long。事实上,我会继续替换此代码示例中的所有 var 用法; var 的便利性在这里丢失了导致类型相关的错误。

【讨论】:

  • 明白了,问题是我在 long 中添加了一个浮点数。关于为什么总和开始失败的任何见解?或者它只是一个通过运行总和结转的精度误差?
  • 如果数字足够大,肯定会。浮点数有一个固定的“有效位”;无论数字的大小如何,只有前 X 个二进制数字可以准确表示。因此,在足够高的值(对于 24 位或大约 7 个十进制数字的“单精度”浮点数),您将开始看到最低有效数字被截断。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多