經典傅里葉演算法小集合 附完整c代碼

来源:https://www.cnblogs.com/cpuimage/archive/2018/08/09/9452536.html
-Advertisement-
Play Games

前面寫過關於傅里葉演算法的應用例子。 《基於傅里葉變換的音頻重採樣演算法 (附完整c代碼)》 當然也就是舉個例子,主要是學習傅里葉變換。 這個重採樣思路還有點瑕疵, 稍微改一下,就可以支持多通道,以及提升性能。 當然思路很簡單,就是切分,合併。 留個作業哈。 本文不講過多的演算法思路,傅里葉變換的各種變種 ...


 前面寫過關於傅里葉演算法的應用例子。

基於傅里葉變換的音頻重採樣演算法 (附完整c代碼)

當然也就是舉個例子,主要是學習傅里葉變換。

這個重採樣思路還有點瑕疵,

稍微改一下,就可以支持多通道,以及提升性能。

當然思路很簡單,就是切分,合併。

留個作業哈。

本文不講過多的演算法思路,傅里葉變換的各種變種,

絕大多數是為提升性能,支持任意長度而作。

當然各有所長,

當時提到參閱整理的演算法:

https://github.com/cpuimage/StockhamFFT

https://github.com/cpuimage/uFFT

https://github.com/cpuimage/BluesteinCrz

https://github.com/cpuimage/fftw3

例如 : 

Stockham 是優化速度, 

BluesteinCrz 是支持任意長度,

uFFT是經典實現。

當然,各有利弊,精度也不一。

最近一直對傅里葉演算法沒放手。

還是想要抽點時間,不依賴第三方庫,實現一份不差於fftw的演算法,

既要保證精度,又要保證性能,同時還要支持任意長度。

目前還在進行中,目前項目完成了45%左右。

越是學習,看的資料林林總總,越覺得傅里葉變換的應用面很廣。

花點時間,採用純c ,實現了經典的傅里葉演算法,

調整代碼邏輯,慢慢開始有點清晰了。

前人栽樹後人乘涼,為了學習方便,

把本人用純c實現的經典傅里葉演算法開源出來給大家學習。

演算法邏輯寫得簡潔明瞭,我也是儘力了。

當然,可能還有更好的實現思路,更多的改進演算法。

不過,我的目的更多是便於學習和理解演算法。

希望能幫助到一些也在學習傅里葉變換演算法的同學。

貼上完整演算法代碼:

#include <stdio.h>
#include <stdbool.h>
#include <math.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif

typedef struct {
    float real, imag;
} cmplx;

cmplx cmplx_mul_add(const cmplx c, const cmplx a, const cmplx b) {
    const cmplx ret = {
            (a.real * b.real) + c.real - (a.imag * b.imag),
            (a.imag * b.real) + (a.real * b.imag) + c.imag
    };
    return ret;
}

void fft_Stockham(cmplx *input, cmplx *output, size_t n, int flag) {
    size_t half = n >> 1;
    cmplx *tmp = (cmplx *) calloc(sizeof(cmplx), n);
    cmplx *y = (cmplx *) calloc(sizeof(cmplx), n);
    memcpy(y, input, sizeof(cmplx) * n);
    for (size_t r = half, l = 1; r >= 1; r >>= 1) {
        cmplx *tp = y;
        y = tmp;
        tmp = tp;
        float factor_w = -flag * M_PI / l;
        cmplx w = {cosf(factor_w), sinf(factor_w)};
        cmplx wj = {1, 0};
        for (size_t j = 0; j < l; j++) {
            size_t jrs = j * (r << 1);
            for (size_t k = jrs, m = jrs >> 1; k < jrs + r; k++) {
                const cmplx t = {(wj.real * tmp[k + r].real) - (wj.imag * tmp[k + r].imag),
                                 (wj.imag * tmp[k + r].real) + (wj.real * tmp[k + r].imag)};
                y[m].real = tmp[k].real + t.real;
                y[m].imag = tmp[k].imag + t.imag;
                y[m + half].real = tmp[k].real - t.real;
                y[m + half].imag = tmp[k].imag - t.imag;
                m++;
            }
            const float t = wj.real;
            wj.real = (t * w.real) - (wj.imag * w.imag);
            wj.imag = (wj.imag * w.real) + (t * w.imag);
        }
        l <<= 1;
    }
    memcpy(output, y, sizeof(cmplx) * n);
    free(tmp);
    free(y);
}

void fft_radix3(cmplx *in, cmplx *result, size_t n, int flag) {
    if (n < 2) {
        memcpy(result, in, sizeof(cmplx) * n);
        return;
    }
    size_t radix = 3;
    size_t np = n / radix;
    cmplx *res = (cmplx *) malloc(sizeof(cmplx) * n);
    cmplx *f0 = res;
    cmplx *f1 = f0 + np;
    cmplx *f2 = f1 + np;
    for (size_t i = 0; i < np; i++) {
        for (size_t j = 0; j < radix; j++) {
            res[i + j * np] = in[radix * i + j];
        }
    }
    fft_radix3(f0, f0, np, flag);
    fft_radix3(f1, f1, np, flag);
    fft_radix3(f2, f2, np, flag);
    float wexp0 = -2 * (float) M_PI * (flag) / (float) (n);
    cmplx wt = {cosf(wexp0), sinf(wexp0)};
    cmplx w0 = {1, 0};
    for (size_t i = 0; i < np; i++) {
        const float w0r = w0.real;
        w0.real = (w0r * wt.real) - (w0.imag * wt.imag);
        w0.imag = (w0.imag * wt.real) + (w0r * wt.imag);
    }
    cmplx w = {1, 0};
    for (size_t j = 0; j < radix; j++) {
        cmplx wj = w;
        for (size_t k = 0; k < np; k++) {
            result[k + j * np] = cmplx_mul_add(f0[k], cmplx_mul_add(f1[k], f2[k], wj), wj);
            const float wjr = wj.real;
            wj.real = (wjr * wt.real) - (wj.imag * wt.imag);
            wj.imag = (wj.imag * wt.real) + (wjr * wt.imag);
        }
        const float wr = w.real;
        w.real = (wr * w0.real) - (w.imag * w0.imag);
        w.imag = (w.imag * w0.real) + (wr * w0.imag);
    }
    free(res);
}

void fft_radix5(cmplx *x, cmplx *result, size_t n, int flag) {
    if (n < 2) {
        memcpy(result, x, sizeof(cmplx) * n);
        return;
    }
    size_t radix = 5;
    size_t np = n / radix;
    cmplx *res = (cmplx *) calloc(sizeof(cmplx), n);
    cmplx *f0 = res;
    cmplx *f1 = f0 + np;
    cmplx *f2 = f1 + np;
    cmplx *f3 = f2 + np;
    cmplx *f4 = f3 + np;
    for (size_t i = 0; i < np; i++) {
        for (size_t j = 0; j < radix; j++) {
            res[i + j * np] = x[radix * i + j];
        }
    }
    fft_radix5(f0, f0, np, flag);
    fft_radix5(f1, f1, np, flag);
    fft_radix5(f2, f2, np, flag);
    fft_radix5(f3, f3, np, flag);
    fft_radix5(f4, f4, np, flag);
    float wexp0 = -2 * (float) M_PI * (flag) / (float) (n);
    cmplx wt = {cosf(wexp0), sinf(wexp0)};
    cmplx w0 = {1, 0};
    for (size_t i = 0; i < np; i++) {
        const float w0r = w0.real;
        w0.real = (w0r * wt.real) - (w0.imag * wt.imag);
        w0.imag = (w0.imag * wt.real) + (w0r * wt.imag);
    }
    cmplx w = {1, 0};
    for (size_t j = 0; j < radix; j++) {
        cmplx wj = w;
        for (size_t k = 0; k < np; k++) {
            result[k + j * np] = cmplx_mul_add(f0[k], cmplx_mul_add(f1[k], cmplx_mul_add(f2[k],
                                                                                         cmplx_mul_add(f3[k], f4[k],
                                                                                                       wj), wj), wj),
                                               wj);
            const float wjr = wj.real;
            wj.real = (wjr * wt.real) - (wj.imag * wt.imag);
            wj.imag = (wj.imag * wt.real) + (wjr * wt.imag);
        }
        const float wr = w.real;
        w.real = (wr * w0.real) - (w.imag * w0.imag);
        w.imag = (w.imag * w0.real) + (wr * w0.imag);
    }
    free(res);
}

void fft_radix6(cmplx *input, cmplx *output, size_t n, int flag) {
    if (n < 2) {
        memcpy(output, input, sizeof(cmplx) * n);
        return;
    }
    size_t radix = 6;
    size_t np = n / radix;
    cmplx *res = (cmplx *) calloc(sizeof(cmplx), n);
    cmplx *f0 = res;
    cmplx *f1 = f0 + np;
    cmplx *f2 = f1 + np;
    cmplx *f3 = f2 + np;
    cmplx *f4 = f3 + np;
    cmplx *f5 = f4 + np;
    for (size_t i = 0; i < np; i++) {
        for (size_t j = 0; j < radix; j++) {
            res[i + j * np] = input[radix * i + j];
        }
    }
    fft_radix6(f0, f0, np, flag);
    fft_radix6(f1, f1, np, flag);
    fft_radix6(f2, f2, np, flag);
    fft_radix6(f3, f3, np, flag);
    fft_radix6(f4, f4, np, flag);
    fft_radix6(f5, f5, np, flag);
    float wexp0 = -2 * (float) M_PI * (flag) / (float) (n);
    cmplx wt = {cosf(wexp0), sinf(wexp0)};
    cmplx w0 = {1, 0};
    for (size_t i = 0; i < np; i++) {
        const float w0r = w0.real;
        w0.real = (w0r * wt.real) - (w0.imag * wt.imag);
        w0.imag = (w0.imag * wt.real) + (w0r * wt.imag);
    }
    cmplx w = {1, 0};
    for (size_t j = 0; j < radix; j++) {
        cmplx wj = w;
        for (size_t k = 0; k < np; k++) {
            output[k + j * np] = cmplx_mul_add(f0[k], cmplx_mul_add(f1[k], cmplx_mul_add(f2[k],
                                                                                         cmplx_mul_add(f3[k],
                                                                                                       cmplx_mul_add(
                                                                                                               f4[k],
                                                                                                               f5[k],
                                                                                                               wj), wj),
                                                                                         wj), wj), wj);
            const float wjr = wj.real;
            wj.real = (wjr * wt.real) - (wj.imag * wt.imag);
            wj.imag = (wj.imag * wt.real) + (wjr * wt.imag);
        }
        const float wr = w.real;
        w.real = (wr * w0.real) - (w.imag * w0.imag);
        w.imag = (w.imag * w0.real) + (wr * w0.imag);
    }
    free(res);
}

void fft_radix7(cmplx *x, cmplx *result, size_t n, int flag) {
    if (n < 2) {
        memcpy(result, x, sizeof(cmplx) * n);
        return;
    }
    size_t radix = 7;
    size_t np = n / radix;
    cmplx *res = (cmplx *) calloc(sizeof(cmplx), n);
    cmplx *f0 = res;
    cmplx *f1 = f0 + np;
    cmplx *f2 = f1 + np;
    cmplx *f3 = f2 + np;
    cmplx *f4 = f3 + np;
    cmplx *f5 = f4 + np;
    cmplx *f6 = f5 + np;
    for (size_t i = 0; i < np; i++) {
        for (size_t j = 0; j < radix; j++) {
            res[i + j * np] = x[radix * i + j];
        }
    }
    fft_radix7(f0, f0, np, flag);
    fft_radix7(f1, f1, np, flag);
    fft_radix7(f2, f2, np, flag);
    fft_radix7(f3, f3, np, flag);
    fft_radix7(f4, f4, np, flag);
    fft_radix7(f5, f5, np, flag);
    fft_radix7(f6, f6, np, flag);
    float wexp0 = -2 * (float) M_PI * (flag) / (float) (n);
    cmplx wt = {cosf(wexp0), sinf(wexp0)};
    cmplx w0 = {1, 0};
    for (size_t i = 0; i < np; i++) {
        const float w0r = w0.real;
        w0.real = (w0r * wt.real) - (w0.imag * wt.imag);
        w0.imag = (w0.imag * wt.real) + (w0r * wt.imag);
    }
    cmplx w = {1, 0};
    for (size_t j = 0; j < radix; j++) {
        cmplx wj = w;
        for (size_t k = 0; k < np; k++) {
            result[k + j * np] = cmplx_mul_add(f0[k], cmplx_mul_add(f1[k], cmplx_mul_add(f2[k],
                                                                                         cmplx_mul_add(f3[k],
                                                                                                       cmplx_mul_add(
                                                                                                               f4[k],
                                                                                                               cmplx_mul_add(
                                                                                                                       f5[k],
                                                                                                                       f6[k],
                                                                                                                       wj),
                                                                                                               wj), wj),
                                                                                         wj), wj), wj);
            const float wjr = wj.real;
            wj.real = (wjr * wt.real) - (wj.imag * wt.imag);
            wj.imag = (wj.imag * wt.real) + (wjr * wt.imag);
        }
        const float wr = w.real;
        w.real = (wr * w0.real) - (w.imag * w0.imag);
        w.imag = (w.imag * w0.real) + (wr * w0.imag);
    }
    free(res);
}

void fft_Bluestein(cmplx *input, cmplx *output, size_t n, int flag) {
    size_t m = 1 << ((unsigned int) (ilogbf((float) (2 * n - 1))));
    if (m < 2 * n - 1) {
        m <<= 1;
    }
    cmplx *y = (cmplx *) calloc(sizeof(cmplx), 3 * m);
    cmplx *w = y + m;
    cmplx *ww = w + m;
    float a0 = (float) M_PI / n;
    w[0].real = 1;
    if (flag == -1) {
        y[0].real = input[0].real;
        y[0].imag = -input[0].imag;
        for (size_t i = 1; i < n; i++) {
            const float wexp = a0 * i * i;
            w[i].real = cosf(wexp);
            w[i].imag = sinf(wexp);
            w[m - i] = w[i];
            y[i].real = (input[i].real * w[i].real) - (input[i].imag * w[i].imag);
            y[i].imag = (-input[i].imag * w[i].real) - (input[i].real * w[i].imag);
        }
    } else {
        y[0].real = input[0].real;
        y[0].imag = input[0].imag;
        for (size_t i = 1; i < n; i++) {
            const float wexp = a0 * i * i;
            w[i].real = cosf(wexp);
            w[i].imag = sinf(wexp);
            w[m - i] = w[i];
            y[i].real = (input[i].real * w[i].real) + (input[i].imag * w[i].imag);
            y[i].imag = (input[i].imag * w[i].real) - (input[i].real * w[i].imag);
        }
    }
    fft_Stockham(y, y, m, 1);
    fft_Stockham(w, ww, m, 1);
    for (size_t i = 0; i < m; i++) {
        const float r = y[i].real;
        y[i].real = (r * ww[i].real) - (y[i].imag * ww[i].imag);
        y[i].imag = (y[i].imag * ww[i].real) + (r * ww[i].imag);
    }
    fft_Stockham(y, y, m, -1);
    float scale = 1.0f / m;
    if (flag == -1) {
        for (size_t i = 0; i < n; i++) {
            output[i].real = ((y[i].real * w[i].real) + (y[i].imag * w[i].imag)) * scale;
            output[i].imag = -((y[i].imag * w[i].real) - (y[i].real * w[i].imag)) * scale;
        }
    } else {
        for (size_t i = 0; i < n; i++) {
            output[i].real = ((y[i].real * w[i].real) + (y[i].imag * w[i].imag)) * scale;
            output[i].imag = ((y[i].imag * w[i].real) - (y[i].real * w[i].imag)) * scale;
        }
    }
    free(y);
}

size_t base(size_t n) {
    size_t t = n & (n - 1);
    if (t == 0) {
        return 2;
    }
    for (size_t i = 3; i <= 7; i++) {
        size_t n2 = n;
        while (n2 % i == 0) {
            n2 /= i;
        }
        if (n2 == 1) {
            return i;
        }
    }
    return n;
}

void FFT(cmplx *input, cmplx *output, size_t n) {
    memset(output, 0, sizeof(cmplx) * n);
    if (n < 2) {
        memcpy(output, input, sizeof(cmplx) * n);
        return;
    }
    size_t p = base(n);
    switch (p) {
        case 2:
            fft_Stockham(input, output, n, 1);
            break;
        case 3:
            fft_radix3(input, output, n, 1);
            break;
        case 5:
            fft_radix5(input, output, n, 1);
            break;
        case 6:
            fft_radix6(input, output, n, 1);
            break;
        case 7:
            fft_radix7(input, output, n, 1);
            break;
        default:
            fft_Bluestein(input, output, n, 1);
            break;
    }
}

void IFFT(cmplx *input, cmplx *output, size_t n) {
    memset(output, 0, sizeof(cmplx) * n);
    if (n < 2) {
        memcpy(output, input, sizeof(cmplx) * n);
        return;
    }
    size_t p = base(n);
    switch (p) {
        case 2:
            fft_Stockham(input, output, n, -1);
            break;
        case 3:
            fft_radix3(input, output, n, -1);
            break;
        case 5:
            fft_radix5(input, output, n, -1);
            break;
        case 6:
            fft_radix6(input, output, n, -1);
            break;
        case 7:
            fft_radix7(input, output, n, -1);
            break;
        default: {
            fft_Bluestein(input, output, n, -1);
            break;
        }
    }
    float scale = 1.0f / n;
    for (size_t i = 0; i < n; i++) {
        output[i].real = output[i].real * scale;
        output[i].imag = output[i].imag * scale;
    }
}

int main() {
    printf("Fast Fourier Transform\n");
    printf("blog: http://cpuimage.cnblogs.com/\n");
    printf("A Simple and Efficient FFT Implementation in C");
    size_t N = 513;
    cmplx *input = (cmplx *) calloc(sizeof(cmplx), N);
    cmplx *output = (cmplx *) calloc(sizeof(cmplx), N);
    for (size_t i = 0; i < N; ++i) {
        input[i].real = i;
        input[i].imag = 0;
    }
    for (size_t i = 0; i < N; ++i) {
        printf("(%f %f) \t", input[i].real, input[i].imag);
    }
    for (int i = 0; i < 100; i++) {
        FFT(input, output, N);
    }
    printf("\n");
    IFFT(output, input, N);
    for (size_t i = 0; i < N; ++i) {
        printf("(%f %f) \t", input[i].real, input[i].imag);
    }
    free(input);
    free(output);
    getchar();
    return 0;
}

項目地址:

https://github.com/cpuimage/cpuFFT

想了好久都沒想到取啥名字好,最後還是選擇了cpu這個首碼。

以上,權當拋磚引玉。

若有其他相關問題或者需求也可以郵件聯繫俺探討。

郵箱地址是: 
[email protected]


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 導言: 我們有時候需要將做好的Python程式打包成為一個exe , 方便我們使用,查找了資料發現 pyinstaller 、py2exe,最後還是選擇的pyinstaller,用的時候踩過了挺多的坑,在這裡記錄一下具體的用法。安裝的時候直接在cmd pip install pyinstaller ...
  • 需求出現/使用場景: 因為公司需要將word辦的介面文檔線上化,看起來是個很好的事情,但是就是苦逼了我們這些幹活的,其中工程量最大的就是參數的錄入,要是參數少也罷,有的介面動輒三四十個參數,更甚八九十個,我手動複製了一個三四十個的就讓我懷疑人生,我覺的我的人生不能在賦值介面參數中浪費掉。以前也學過一 ...
  • 記憶體限制:256 MiB 時間限制:2000 ms 標準輸入輸出 題目類型:傳統 評測方式:文本比較 上傳者: 匿名 【題目描述】 這是一道模板題。 維護一個 nnn 點的無向圖,支持: 加入一條連接 uuu 和 vvv 的無向邊 查詢 uuu 和 vvv 的連通性 由於本題數據較大,因此輸出的時候 ...
  • 面向對象 面向過程的代表主要是 語言,面向對象是相對面向過程而言, 是面向對象的編程語言,面向過程是通過函數體現,面向過程主要是功能行為。 而對於面向對象而言,將功能封裝到對象,所以面向對象是基於面向過程的。以前是主要以面向過程為思想,現在是將功能裝進對象中,如果要用到功能時,就調用該對象即可。 面 ...
  • 數組的綜合應用 冒泡排序 為什麼這個排序要叫冒泡呢?為什麼不叫其他名詞呢? 其實這個取名是根據排序演算法的基本思路命名的,見名知意,冒泡排序,就是想泡泡在水裡一樣,在水裡大的泡泡先浮出水面,就是大的先排出來,最小的最慢排出。 冒泡排序,是對排序的各個元素從頭到尾依次進行相鄰的大小比較, 比如你是隊長, ...
  • 給定一個僅包含大小寫字母和空格 ' ' 的字元串,返回其最後一個單詞的長度。 如果不存在最後一個單詞,請返回 0 。 說明:一個單詞是指由字母組成,但不包含任何空格的字元串。 示例: 輸入: "Hello World" 輸出: 5 給定一個僅包含大小寫字母和空格 ' ' 的字元串,返回其最後一個單詞 ...
  • 給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。 示例: 輸入: [-2,1,-3,4,-1,2,1,-5,4], 輸出: 6 解釋: 連續子數組 [4,-1,2,1] 的和最大,為 6。 class Solution: def maxSubAr ...
  • 前言 SqlSession是Mybatis最重要的構建之一,可以簡單的認為Mybatis一系列的配置目的是生成類似 JDBC生成的Connection對象的SqlSession對象,這樣才能與資料庫開啟“溝通”,通過SqlSession可以實現增刪改查(當然現在更加推薦是使用Mapper介面形式), ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...