C++ 针对给定问题的优化算法?
我正在解决一个问题,这个问题表明我们有一个包含从1到N的整数的列表L。我们必须执行以下操作N−1次:C++ 针对给定问题的优化算法?,c++,optimization,C++,Optimization,我正在解决一个问题,这个问题表明我们有一个包含从1到N的整数的列表L。我们必须执行以下操作N−1次: 选择列表中的两个元素,让我们用X和Y表示它们 从L中删除所选元素 将数字X+Y+X*Y附加到L。 最后,L正好包含一个整数。找到这个整数。 因为答案可能很大,我们必须计算它的模10^9+7 限制条件: 1.≤N≤一百万 时限: 1秒 我已经写了这段代码,它在线性时间内给出了正确的答案,但它说这种方法超出了时间限制。有人能提供更好的优化解决方案吗 inline ull cal(ull x, ull
inline ull cal(ull x, ull y){
ull ans, i, modno;
modno = 1000000007;
i = 1;
ans = (x + y);
i = (i*x) % modno;
i = (i*y) % modno;
ans = ans + i;
ans = ans % modno;
return ans;
}
int main(){
ull n;
cin>>n;
ull sum, modno;
sum = 0;
modno = 1000000007;
if(n == 1)
cout<<1<<endl;
else
{
sum = n + (n-1) + (n*(n-1));
n -= 2;
do
{
if(n <= 0)
break;
sum = cal(sum, n);
n -= 1;
}while(1);
cout<<ans<<endl;
}
return 0;
}
内联ull校准(ull x,ull y){
乌兰斯,我,莫德诺;
modno=100000007;
i=1;
ans=(x+y);
i=(i*x)%modno;
i=(i*y)%modno;
ans=ans+i;
ans=ans%modno;
返回ans;
}
int main(){
ulln;
cin>>n;
全和,莫德诺;
总和=0;
modno=100000007;
如果(n==1)
cout求和有一个封闭形式的解:L=(N+1)!-1
求和遵循此循环方程L_N=N+L_(N-1)+N*L_(N-1),L_0=0
,只需始终选择X=L_(N-1)
和Y=N
(=要添加的下一个数字)即可获得
派生:
编辑:
当您发布最终代码时,我将发布我的基准:
#include <iostream>
#include <cstdint>
#include <chrono>
std::uint64_t
factorial(std::uint64_t n) {
std::uint64_t x = 1;
while (n > 1)
x = (x * n--) % 1'000'000'007;
return x;
}
int
main() {
std::uint64_t n;
std::cin >> n;
std::uint64_t numMicro = 0;
for (std::size_t i = 0; i < 1'000; ++i) {
auto start = std::chrono::high_resolution_clock::now();
volatile std::uint64_t res = factorial(n);
auto end = std::chrono::high_resolution_clock::now();
numMicro +=
std::chrono::duration_cast<std::chrono::microseconds>(end - start)
.count();
}
std::cout << "On average: " << numMicro / 1000.0 << "microseconds";
return 0;
}
#包括
#包括
#包括
标准:uint64\U t
阶乘(标准::uint64\u t n){
标准:uint64_t x=1;
而(n>1)
x=(x*n--)%1'000'000'007;
返回x;
}
int
main(){
标准:uint64\u t n;
标准:cin>>n;
标准:uint64\u t numMicro=0;
对于(标准:尺寸i=0;i<1'000;++i){
自动启动=标准::时钟::高分辨率时钟::现在();
挥发性标准:uint64_t res=阶乘(n);
自动结束=标准::时钟::高分辨率时钟::现在();
努米克罗+=
标准::计时::持续时间(结束-开始)
.count();
}
std::cout算法应该如下所示:
sum问题只是说“选择列表中的两个元素,让我们用X和Y表示它们。
”没有说明元素需要选择的顺序
因此,可以将其改写为:
将列表拆分为每个CPU的一个子列表
使用SIMD;为每个CPU中的每对计算(X+1)*(Y+1)
子列表并将结果作为64位整数存储在新列表中
可以避免进行昂贵的模运算
使用SIMD;计算中每对的(X*Y-1)%100000007
每个CPU的新子列表,并将结果存储为32位整数
重复前面的2个步骤,直到剩下一个值
每个CPU(如果需要将其恢复到32位,则执行最后的R=(R-1)%100000007
)
值,并终止除一个线程以外的所有线程
使用SIMD;计算每对的(X+1)*(Y+1)
使用SIMD;计算每对的(X+*Y-1)%100000007
重复前面的2个步骤,直到只剩下一个值
正如其他人所提到的,这个问题归结为计算((n+1)!-1)%p。你可以四处搜索做这件事的快速方法(快速阶乘模素数)。在1s下工作的方法之一就是上面提到的方法
更新:刚刚检查了codechef中的问题链接。与往常一样,诀窍在于您没有准确描述的约束。您必须对多达100000个案例执行相同的任务。使用standard for loop可以在1秒内获得单个事实(n)mod p,因为n很小
不起作用的是为每个测试用例计算事实(n)mod p。与许多其他问题一样,使用预计算可以从中受益:构建一个数组,其中arr[i]是i!mod p到i=最大值n可以取+1。有了这些信息,您可以通过返回(arr[n+1]-1)%p来回答O(1)中的每个查询(测试用例)
下一次,请在你的描述中添加问题链接,通常情况下,你认为某件事与问题无关,而这一部分就是问题的全部答案。考虑一下,如果在3)中你只需要使用X+Y
,那么解决方案是什么,然后尝试以小步骤进行处理(例如,X+Y+c
对于某些常量c
,什么样的解决方案?)性能提升功能更适合于“”。要问的问题是,万一有人对OP描述中遗漏的条件有点好奇。添加的最终代码可能会重复,这也会超出时间限制。有人能为这个问题提供一个有效的实现吗?我也尝试过像威尔逊定理这样的快速阶乘算法。也许是真的ds计算阶乘的另一种方法我仍然需要为每次迭代计算它模10^9+7,因为数字很大,这再次超过了时间限制。对此有什么建议吗?@Sam我非常怀疑有更紧凑的解决方案,但由于10^9+7是素数,这个问题适用。@Sam你介意在你的问题中添加最后的代码吗在上,因为我对N=1e6的朴素阶乘实现只需要~4ms。@Sam发布了我的基准测试,我恐怕做不到比这个更好的答案。你能提供一个实现吗?因为我的实现似乎给出了一些错误。即使使用快速方法,时间限制也超过了,可能有不同的方法,如果你需要,请帮助我tsolution@sam你试过哪一个?我试过使用威尔逊定理,但仍然超出了时间限制。无法实现你评论中给出的那些,如果你能提供实现,那将是一个很大的帮助。谢谢
#include <iostream>
#include <cstdint>
#include <chrono>
std::uint64_t
factorial(std::uint64_t n) {
std::uint64_t x = 1;
while (n > 1)
x = (x * n--) % 1'000'000'007;
return x;
}
int
main() {
std::uint64_t n;
std::cin >> n;
std::uint64_t numMicro = 0;
for (std::size_t i = 0; i < 1'000; ++i) {
auto start = std::chrono::high_resolution_clock::now();
volatile std::uint64_t res = factorial(n);
auto end = std::chrono::high_resolution_clock::now();
numMicro +=
std::chrono::duration_cast<std::chrono::microseconds>(end - start)
.count();
}
std::cout << "On average: " << numMicro / 1000.0 << "microseconds";
return 0;
}