CodeJam 最小标量产品问题

Posted

技术标签:

【中文标题】CodeJam 最小标量产品问题【英文标题】:CodeJam Minimum Scalar Product Issue 【发布时间】:2016-05-21 23:46:56 【问题描述】:

我在练习这个问题,很快就找到了正确的算法,但是在实现它时,我遇到了一些奇怪的事情。起初,我意识到我被整数类型的溢出所困扰,所以开始使用 __int64。那是我注意到下一个奇怪的事情的时候。所以首先,这是我的代码...

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <numeric>

using namespace std;

const string cInputFileName = "E:\\CodeJamInputs\\d-large-practice.in";
const string cOutputFileName = "E:\\CodeJamInputs\\d-large-practice.out.txt";

__int64 FindSmallestProductOfSums(const vector<int> &iVec1, const vector<int> &iVec2)

    vector<int> v1 = iVec1;
    vector<int> v2 = iVec2;

    sort(v1.begin(), v1.end());
    sort(v2.begin(), v2.end(), greater<int>());

    __int64 productOfSumsA = inner_product(v1.begin(), v1.end(), v2.begin(), 0);

    __int64 productOfSumsB = 0;
    for(vector<int>::size_type i = 0; i < v1.size(); ++i)
        productOfSumsB += (__int64)v1[i] * (__int64)v2[i];

    return productOfSumsB;


int _tmain(int argc, _TCHAR* argv[])

    ifstream inputFile(cInputFileName, ifstream::in);
    ofstream outputFile(cOutputFileName, ofstream::out);

    if(inputFile.is_open() && outputFile.is_open())
    
        int numCases;
        inputFile >> numCases;

        for(int i = 0; i < numCases; ++i)
        
            int vectorSizes;
            inputFile >> vectorSizes;

            vector<int> vec1, vec2;

            for(int j = 0; j < vectorSizes; ++j)
            
                int value;
                inputFile >> value;
                vec1.push_back(value);
            

            for(int j = 0; j < vectorSizes; ++j)
            
                int value;
                inputFile >> value;
                vec2.push_back(value);
            

            __int64 smallestProductOfSums = FindSmallestProductOfSums(vec1, vec2);

            outputFile << "Case #" << (i + 1) << ": " << smallestProductOfSums;
            outputFile << endl;
        
    

    inputFile.close();
    outputFile.close();

所以,如您所见,我对两个向量进行了两次计算。一种使用 STL inner_product,另一种只是手动迭代。那么愚蠢的是,对于问题中的大数据集,inner_product 方法导致错误的返回,而手动的方法是正确的。进入 STL 代码,在我看来确实发生了溢出,因为 Ty Val 变量似乎是一个 int,这当然是结果被累积的地方。

那么,我想知道的是,对于那些使用 inner_product 解决了这个问题的人,你认为区别是什么?我尝试将 0LL 作为初始参数传递给咯咯笑,实际上,它确实导致了不同的答案,但仍然不是正确的答案。奇怪的是,在我添加显式 __int64 强制转换之前,它确实得到了与手动方法相同的答案。所以这里肯定有一些奇怪的类型和溢出,只是不确定是什么。无论如何,对于小型和大型系列,我都得到了正确的答案,但我只是看到有些人使用了 inner_product,我无法让它发挥作用。让我重新表述一下...inner_product 适用于小型数据集,但不适用于大型数据集,因为我的手动解决方案适用于小型和大型数据集。

以下是问题中每个案例的输出(大型数据集总共 10 个)。对于每种情况,都有三个输出。第一个是使用手动计算的方法(正确答案),第二个是使用 init_product 的 init 为“0”(错误答案),第三个是使用 inner_product 的 init 为“0LL”(错误答案)。另外,凭直觉,我也编译为 x64 目标,但结果是一样的。

案例#1:-7839202227936 案例#1:-886912736 案例#1:-1104693507808

案例#2:7999201712083 案例二:1972606931 案例二:1127254038483

案例#3:-1313429236847 案例#3:830755729 案例#3:-175262903407

案例#4:-3710387739618 案例#4:464004126 案例#4:-89730309090

案例#5:-3414920765916 案例#5:-421765596 案例#5:-82026144220

案例#6:-1271937742993 案例#6:-627423377 案例#6:-30692194449

案例 #7:-1964394407029 案例#7:-1594352757 案例 #7:-40249058421

案例#8:-1884282427866 案例#8:1208215078 案例#8:-101871000026

案例 #9:-4044533757860 案例#9:1325434972 案例#9:-106048747428

案例 #10:-838783451371 案例 #10:-1264828651 案例 #10:-44214501611

抱歉,这篇文章很长,但我认为这是一个有趣的问题。

【问题讨论】:

【参考方案1】:

CPP reference 包含 inner_product 的示例实现,如下所示:

...
value = value + *first1 * *first2;
...

inner_product涉及两种类型:

    累加器的类型(从传入的初始值推导出来,例如0LL) 被相乘的元素的类型(从数组的类型推导出来)

因此,在您的情况下,累加器有一个 int64,但元素只有 int(可能是 32 位数据类型)。当乘法发生时,乘法的结果以 32 位精度计算!

因此,有两个可能的溢出位置,一个在 + 中,一个在 * 中。更改初始值会修复累加器,但您仍然面临乘法溢出的风险。

解决此问题的最简单方法是将输入数组更改为 int64 数组而不是 int。在这种情况下,乘法将以 64 位精度完成。

【讨论】:

以上是关于CodeJam 最小标量产品问题的主要内容,如果未能解决你的问题,请参考以下文章

CodeJam Qualification Round 2022

CodeJam Qualification Round 2022

CodeJam Qualification Round 2022

Google CodeJam浴室档位2017年资格赛圆形大数据集错误

google kcikstart 2018 round c

[leetcode] 407. Trapping Rain Water II