您的位置:

沃尔什变换

一、沃尔什变换公式

沃尔什变换(Walsh Transform),又称Walsh-Hadamard变换,它是一种不带权的正交变换,是傅里叶变换的一种实现方式。

其变换公式如下:

W(u,v) = (-1)^{f(u,v)} \prod_{i=0}^{n-1}w_i(u_i,v_i)

其中:

  • u,v为整数,0≤u,v≤n-1
  • f(u,v)为u、v的二进制位按位异或的结果
  • w_i是Walsh函数,计算方式为:
w_0(x,y) = 1, x,y∈{0,1}
w_i(x,y) = w_{i-1}(x,y)w_{i-1}(x,y⊕2^{i-1}), i≥1

其中 ⊕ 表示按位异或操作。

二、沃尔什变换怎么求

在计算机中,沃尔什变换可以用递归方式实现。

以计算2次沃尔什变换为例:

//计算变换矩阵
int w[4][4];
int WalshMatrix(int x, int y, int n)
{
    if (n == 1)
    {
        w[x][y] = 1;
        return 1;
    }
    int t = n / 2;
    WalshMatrix(x, y, t);
    WalshMatrix(x + t, y, t);
    WalshMatrix(x, y + t, t);
    WalshMatrix(x + t, y + t, t);

     //递归计算变换
     for (int i = 0; i < t; i++)
     {
        for (int j = 0; j < t; j++)
        {
            int a = w[x + i][y + j];
            int b = w[x + i][y + j + t];
            int c = w[x + i + t][y + j];
            int d = w[x + i + t][y + j + t];
            w[x + i][y + j] = a + b + c - d;
            w[x + i][y + j + t] = a - b + c + d;
            w[x + i + t][y + j] = a + b - c - d;
            w[x + i + t][y + j + t] = a - b - c + d;
         }
     }
}

//调用接口
WalshMatrix(0, 0, 4);

其中,变换矩阵存放在w数组中。

三、沃尔什变换矩阵

沃尔什变换矩阵是一个n×n的矩阵。在上一节中,我们已经介绍了如何计算沃尔什变换矩阵。

例如,当n=4时,沃尔什变换矩阵如下:

1  1  1  1
1 -1  1 -1
1  1 -1 -1
1 -1 -1  1

一个大小为n的Walsh矩阵可以通过将一个大小为2的Walsh矩阵的每个元素除以2,然后将四个2x2矩阵插入到新矩阵的相应位置来生成。当n是2的幂时,可以通过运行时间为O(n log n)的递归算法来计算Walsh矩阵。

四、沃尔什变换可分离

沃尔什变换具有可分离性,即一个n维向量的Walsh变换可以被分解成一维向量的Walsh变换的张量积。

例如,假设我们有一个4×4的Walsh矩阵A,行向量V和列向量U,其中V和U的元素都是±1。因此,如果Y是向量V与U的张量积,那么Y的Walsh变换是矩阵乘积A * V * U'。

五、沃尔什变换核

沃尔什变换核是一个n×n的01矩阵。当n为奇数时,沃尔什变换核的第一行和第一列全为1,其他位置值为0和1交替。当n为偶数时,沃尔什变换核可以通过将大小为n/2的沃尔什变换核缩放2倍并插入到大小为n的核的对应位置来构造。

例如,当n=4时,沃尔什变换矩阵如下:

1  1  1  1
1 -1  1 -1
1  1 -1 -1
1 -1 -1  1

对应的核为:

1 1 0 0
1 1 0 0
0 0 1 1
0 0 1 1

六、沃尔什变换核矩阵怎么算

可以通过下面的递归算法计算沃尔什变换核矩阵:

void WalshCore(int core[][N], int n)
{
    if (n == 1)
    {
        core[0][0] = 1;
        return;
    }
    int t = n / 2;
    WalshCore(core, t);
    for (int i = 0; i < t; i++)
    {
        for (int j = 0; j < t; j++)
        {
            core[i][j + t] = core[i + t][j] = core[i + t][j + t] = -core[i][j];
        }
    }
}

其中,核矩阵存放在core数组中。

七、沃尔什变换系数

沃尔什变换系数是一个大小为n的向量,其中第i个分量为沃尔什函数wi的第i个输入和第n个输入的差的倒数。

例如,当n=4时,沃尔什变换系数是:

1 1/3 1/3 1/3

八、沃尔什变换核矩阵怎么算

可以通过下面的递归算法计算沃尔什变换的系数:

void WalshCoeff(double coeff[], int n)
{
    if (n == 1)
    {
        coeff[0] = 1;
        return;
    }
    int t = n / 2;
    WalshCoeff(coeff, t);
    for (int i = 0; i < t; i++)
    {
        coeff[i + t] = coeff[i] / -2;
    }
}

其中,系数存放在coeff数组中。

九、沃尔什变换计算题

下面是一个沃尔什变换的计算题:

设f(x)为长度为2^n的01数列,定义g(x)为:g(x) = Sum(f(y) * (-1)^(x⊕y),y∈[0,2^n-1])。请计算g的沃尔什变换。

通过分析题目中的g函数,可以发现它是f函数的沃尔什变换的直接表达式。因此,问题可以转化为计算f的沃尔什变换,然后用系数对其进行加权求和得到g的沃尔什变换。

沃尔什变换的计算方法已经在前面的章节中介绍了,下面给出计算g的代码示例:

const int N = 1 << 22;
int n;
int a[N],b[N];
double c[N];

void Solve(int l, int r)     //计算f的沃尔什变换
{
    if (l == r) {c[l] = a[l]; return;}
    int mid = l + r >> 1;
    Solve(l, mid), Solve(mid + 1, r);
    memcpy(b, a + l, (r - l + 1) * sizeof(int));
    int p = mid - l + 1;
    for (int i = 0; i < p; i++)
        for (int j = 0; j < (1 << ((int)(log2(r - l + 1) - .5) + 1)); j += 2 * p)
        {
            int x = b[i + j], y = b[i + j + p];
            b[i + j] = x + y, b[i + j + p] = x - y;
        }
    memcpy(c + l, b, (r - l + 1) * sizeof(double));
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < (1 << n); ++i)
        scanf("%d", &a[i]);
    Solve(0, (1 << n) - 1);
    WalshCoeff(c, (1 << n));      //计算Walsh变换系数
    for (int i = 0; i < (1 << n); i++)
        printf("%lf\n", c[i]);
    return 0;
}

十、沃尔什变换例题

下面是一些沃尔什变换的例题:

例题1:POJ 2752 Shuffle'm Up

题目描述:

给你一个长度为2^n的数列a,这个数列的前一半是a[0], ...,a[2^(n-1)-1],后一半是a[2^(n-1)], ..., a[2^n-1]。并且,对于i∈[0,2^(n-1)-1],有f(i)=a[2i]和f(i+2^(n-1))=a[2i+1];对于i∈[2^(n-1),2^n-1],有f(i)=a[(i-2^n/2)*2+1]和f(i-2^(n-1))=a[(i-2^n/2)*2]。问最后的数列是什么。

输入格式:

第一行一个整数n。接下来的一行包含2^n个整数,表示数列a。

输出格式:

输出一个字符串,表示最后的数列。

解题思路:

参考洗牌算法的思路,将序列分成两部分,然后递归地执行沃尔什变换和乘法运算,最后得到新的序列。

代码如下:

int n;
int a[MAXN];

void shuffle(int l, int r)
{
    if (r - l == 1)
        return;
    int mid = (l + r + 1) >> 1;
    for (int i = 0; i <= mid - l - 1; i++)
    {
        swap(a[l + i], a[mid + i]);
    }
    shuffle(l, mid);
    shuffle(mid, r);
    for (int i = l; i < r; i += 2)
    {
        int x = a[i], y = a[i + 1];
        a[i] = (x + y) / 2, a[i + 1] = (x - y) / 2;
    }
}

int main()
{
    scanf("%d", &n);
    int len = (1 << n);
    for (int i = 0; i < len; i++)
        scanf("%d", &a[i]);
    shuffle(0, len);
    for (int i = 0; i < len; i++)
        putchar(a[i] + 'A