标题:c语言写的完整程序看不懂请老师逐行解释一下,谢谢!
只看楼主
ysr2857
Rank: 6Rank: 6
等 级:贵宾
威 望:28
帖 子:767
专家分:65
注 册:2020-2-10
结帖率:100%
已结贴  问题点数:20 回复次数:12 
c语言写的完整程序看不懂请老师逐行解释一下,谢谢!
下面是网上找到的用vc编程的fft快速高精度乘法程序,我看不懂,请老师看看,逐行解释一下,或翻译成vb可调用程序,谢谢!
#include<bits/stdc++.h>
using namespace std;
//complex是stl自带的定义复数的容器
typedef complex<double> cp;
#define N 2097153
//pie表示圆周率π
const double pie=acos(-1);
int n;
cp a[N],b[N];
int rev[N],ans[N];
char s1[N],s2[N];
//读入优化
int read(){
    int sum=0,f=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return sum*f;
}
//初始化每个位置最终到达的位置
{
    int len=1<<k;
    for(int i=0;i<len;i++)
    rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
}
//a表示要操作的系数,n表示序列长度
//若flag为1,则表示FFT,为-1则为IFFT(需要求倒数)
void fft(cp *a,int n,int flag){
    for(int i=0;i<n;i++)
    {
     //i小于rev[i]时才交换,防止同一个元素交换两次,回到它原来的位置。
      if(i<rev[i])swap(a[i],a[rev[i]]);
    }
    for(int h=1;h<n;h*=2)//h是准备合并序列的长度的二分之一
    {
    cp wn=exp(cp(0,flag*pie/h));//求单位根w_n^1
     for(int j=0;j<n;j+=h*2)//j表示合并到了哪一位
     {
      cp w(1,0);
       for(int k=j;k<j+h;k++)//只扫左半部分,得到右半部分的答案
       {
         cp x=a[k];
         cp y=w*a[k+h];
         a[k]=x+y;  //这两步是蝴蝶变换
         a[k+h]=x-y;
         w*=wn; //求w_n^k
       }
     }
     }
     //判断是否是FFT还是IFFT
     if(flag==-1)
     for(int i=0;i<n;i++)
     a[i]/=n;
}
int main(){
    n=read();
    scanf("%s%s",s1,s2);
    //读入的数的每一位看成多项式的一项,保存在复数的实部
    for(int i=0;i<n;i++)a[i]=(double)(s1[n-i-1]-'0');
    for(int i=0;i<n;i++)b[i]=(double)(s2[n-i-1]-'0');
    //k表示转化成二进制的位数
    int k=1,s=2;
     while((1<<k)<2*n-1)k++,s<<=1;
    init(k);
    //FFT 把a的系数表示转化为点值表示
    fft(a,s,1);
    //FFT 把b的系数表示转化为点值表示
    fft(b,s,1);
    //FFT 两个多项式的点值表示相乘
    for(int i=0;i<s;i++)
    a[i]*=b[i];
    //IFFT 把这个点值表示转化为系数表示
    fft(a,s,-1);
    //保存答案的每一位(注意进位)
    for(int i=0;i<s;i++)
    {
    //取实数四舍五入,此时虚数部分应当为0或由于浮点误差接近0
    ans[i]+=(int)(a[i].real()+0.5);
    ans[i+1]+=ans[i]/10;
    ans[i]%=10;
    }
    while(!ans[s]&&s>-1)s--;
    if(s==-1)printf("0");
    else
    for(int i=s;i>=0;i--)
    printf("%d",ans[i]);
    return 0;
}
搜索更多相关主题的帖子: 表示 i++ for sum int 
2020-03-29 17:58
ysr2857
Rank: 6Rank: 6
等 级:贵宾
威 望:28
帖 子:767
专家分:65
注 册:2020-2-10
得分:0 
请老师看一下这个程序能运行吗?是否真的完整?
2020-03-29 22:59
wmf2014
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:216
帖 子:2039
专家分:11273
注 册:2014-12-6
得分:20 
帮你顶一下吧,希望有相关高手关注到。
你想翻译到vb不切实际,且不说vb没有复数类,就是fft乘法即使能提高速度,也只是得到高精度结果,无法得到完全正确的结果。fft要得到高速,是要牺牲细节的,如果非要做到无损,最后的时间复杂度又是O(n^2)了,无法实现高速。
其次,还是我在vb里和你讨论的,想提高大数乘法速度,vb显然不行,c的传统竖式大数乘法肯定完胜vb快速乘法。
建议你关注分治法的快速乘法,据说也接近O(nlogn)了,参考链接:https://zhuanlan.

能编个毛线衣吗?
2020-03-29 23:00
ysr2857
Rank: 6Rank: 6
等 级:贵宾
威 望:28
帖 子:767
专家分:65
注 册:2020-2-10
得分:0 
回复 3楼 wmf2014
谢谢您的指导!
vb是可以表示复数的,用到两个或三个数组,写成c=a() “+”b() “i”的形式。
fft的原理我也不懂,转化过程中把带小数点的数值四舍五入都取整数,好像是可以保证整数都正确都准确无误的。
还有一个法是数论变换法,不用复数,直接得到整数。
你发的这个链接我慢慢看看,对我来说很有用,谢谢!
2020-03-30 00:11
ysr2857
Rank: 6Rank: 6
等 级:贵宾
威 望:28
帖 子:767
专家分:65
注 册:2020-2-10
得分:0 
回复 3楼 wmf2014
我发到vb里那篇文章中有个分治法乘法,我试了一下不能用,不知道哪里有错?您有空再看看吧,谢谢!
2020-03-30 00:15
wmf2014
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:216
帖 子:2039
专家分:11273
注 册:2014-12-6
得分:0 
复数类和用数组表示复数是两个概念,比如你提供的代码里有cp(0,pie*flag/h),这是执行了该类的构造函数,谁知道最后是怎么初始化的呢?

能编个毛线衣吗?
2020-03-30 00:26
ysr2857
Rank: 6Rank: 6
等 级:贵宾
威 望:28
帖 子:767
专家分:65
注 册:2020-2-10
得分:0 
回复 6楼 wmf2014
嗯嗯好,是两回事。真不懂,有人说是可以翻译的,不过可能是猜测?谢谢指导!
2020-03-30 00:50
ysr2857
Rank: 6Rank: 6
等 级:贵宾
威 望:28
帖 子:767
专家分:65
注 册:2020-2-10
得分:0 
这个程序能运行吗?速度是否快?结果是否正确?
2020-03-30 08:09
wmf2014
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:216
帖 子:2039
专家分:11273
注 册:2014-12-6
得分:0 
你提供的代码缺少init函数,后发现有该函数实体,无函数头部,加了,再在网上down了个stdc++头文件后,可以运行。由于不熟悉复数,代码的头一个执行的read函数就不知道输入什么,我输入了“1234”,后面两个乱输入的执行结果也不知道结果是否正确,调试正常后的代码及运行结果如下:
程序代码:
#include<stdc++.h>
using namespace std;
//complex是stl自带的定义复数的容器
typedef complex<double> cp;
#define N 2097153
//pie表示圆周率π
const double pie = acos(-1);
int n;
cp a[N], b[N];
int rev[N], ans[N];
char s1[N], s2[N];
//读入优化
int read() {
    int sum = 0, f = 1;
    char ch = getchar();
    while (ch > '9' || ch < '0') { if (ch == '-')f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { sum = (sum << 3) + (sum << 1) + ch - '0'; ch = getchar(); }
    return sum * f;
}
//初始化每个位置最终到达的位置
void init(int k)
{
    int len = 1 << k;
    for (int i = 0; i < len; i++)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (k - 1));
}
//a表示要操作的系数,n表示序列长度
//若flag为1,则表示FFT,为-1则为IFFT(需要求倒数)
void fft(cp *a, int n, int flag) {
    for (int i = 0; i < n; i++)
    {
        //i小于rev[i]时才交换,防止同一个元素交换两次,回到它原来的位置。
        if (i < rev[i])swap(a[i], a[rev[i]]);
    }
    for (int h = 1; h < n; h *= 2)//h是准备合并序列的长度的二分之一
    {
        cp wn = exp(cp(0, flag*pie / h));//求单位根w_n^1
        for (int j = 0; j < n; j += h * 2)//j表示合并到了哪一位
        {
            cp w(1, 0);
            for (int k = j; k < j + h; k++)//只扫左半部分,得到右半部分的答案
            {
                cp x = a[k];
                cp y = w * a[k + h];
                a[k] = x + y;  //这两步是蝴蝶变换
                a[k + h] = x - y;
                w *= wn; //求w_n^k
            }
        }
    }
    //判断是否是FFT还是IFFT
    if (flag == -1)
        for (int i = 0; i < n; i++)
            a[i] /= n;
}
int main() {
    n = read();
    scanf("%s%s", s1, s2);
    //读入的数的每一位看成多项式的一项,保存在复数的实部
    for (int i = 0; i < n; i++)a[i] = (double)(s1[n - i - 1] - '0');
    for (int i = 0; i < n; i++)b[i] = (double)(s2[n - i - 1] - '0');
    //k表示转化成二进制的位数
    int k = 1, s = 2;
    while ((1 << k) < 2 * n - 1)k++, s <<= 1;
    init(k);
    //FFT 把a的系数表示转化为点值表示
    fft(a, s, 1);
    //FFT 把b的系数表示转化为点值表示
    fft(b, s, 1);
    //FFT 两个多项式的点值表示相乘
    for (int i = 0; i < s; i++)
        a[i] *= b[i];
    //IFFT 把这个点值表示转化为系数表示
    fft(a, s, -1);
    //保存答案的每一位(注意进位)
    for (int i = 0; i < s; i++)
    {
        //取实数四舍五入,此时虚数部分应当为0或由于浮点误差接近0
        ans[i] += (int)(a[i].real() + 0.5);
        ans[i + 1] += ans[i] / 10;
        ans[i] %= 10;
    }
    while (!ans[s] && s > -1)s--;
    if (s == -1)printf("0");
    else
        for (int i = s; i >= 0; i--)
            printf("%d", ans[i]);
    return 0;
}


三个输入:1234   123456789123456789123456789   999999999999999999999999999999999999
以下是输出:
123456789123456789123456783667-2-2-2-2-2-300-4-1-1-5-2-2000-4-1-1-5-2-2000-4-1-1-5-1-8-5-4-4-4-4-4-4-4-4-4-54444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444504362139864362139864362139573333332995555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555584

能编个毛线衣吗?
2020-03-30 10:51
ysr2857
Rank: 6Rank: 6
等 级:贵宾
威 望:28
帖 子:767
专家分:65
注 册:2020-2-10
得分:0 
回复 9楼 wmf2014
谢谢您!这个结果不对吧?不知道是哪儿有问题了。
2020-03-30 11:15



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-500707-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.399813 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved