中值维护算法 - 相同的实现会产生不同的结果,具体取决于Int32或Int64

时间:2015-03-09 18:34:48

标签: c# algorithm integer int64 int32

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

howework问题要求编码中位数维护算法。

正式声明如下:

  

此问题的目标是实施&#34;中位维护&#34;算法(在关于堆应用程序的第5周讲座中介绍)。文本文件包含未排序顺序的1到10000之间的整数列表;你应该把它视为一个数字流,一个接一个地到达。令x i 表示文件的i th 编号,k th 中间m k 定义为数字的中位数x 1 ,...,x k 。 (所以,如果k是奇数,那么m k 是( th x 1 中的最小数,... ,x k ;如果k是偶数,那么m 1 是x 1 <中的(k / 2) th 最小数/子>,...,X <子>ķ。)

为了获得 O(n)运行时间,这应该明显地使用堆来实现。无论如何,我使用Brute Force编写了这个(截止日期太快,需要立即回答)( O(n 2 )),步骤如下:

  1. 阅读
  2. 中的数据
  3. 排序数组
  4. 查找中位数
  5. 将其添加到运行时间
  6. 我通过几个测试用例(已知答案)运行算法并得到了正确的结果,但是当我在更大的数据集上运行相同的算法时,我得到了错误的答案。我正在使用Int64 ro进行所有操作代表数据。 然后我尝试切换到Int32并且神奇地得到了正确的答案,这对我来说毫无意义。

    代码如下,并且还找到here(数据在回购中)。该算法在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()函数对列表中的所有项进行求和不同。

    结果(第一个是错误的结果): Console Application Results

    这些是计算机详细信息: Computer details

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

    谢谢,

2 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:1)

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