生成总和为给定数字的统一二进制数组

时间:2013-01-10 00:08:12

标签: algorithm

我想生成一些二进制数组来控制没有控制器的LED的亮度。我不会在微控制器上产生脉冲宽度(这会占用应该用于其他事情的指令周期),而是将它们硬编码为查找表,并反复循环遍历二进制数组,设置在指令处开启或关闭LED。

假设我有一个4的数组长度,我将有5个亮度级别:

{{ 0, 0, 0, 0 },
 { 1, 0, 0, 0 },
 { 1, 0, 1, 0 },
 { 1, 1, 1, 0 },
 { 1, 1, 1, 1 }}

请注意,我试图在整个阵列中尽可能均匀地分布1,这并非总是可行,但人眼不应该看到小的异常。

假设我的数组长度为8,我希望亮度为5/8:

{ 1, 0, 1, 1, 0, 1, 1, 0 };

这似乎是一个很好的传播,但我也可以使用:

{ 1, 0, 1, 1, 1, 0, 1, 0 };

哪个更好?当然,平均亮度是相同的。从一个州到另一个州的变化数量也是相同的。标准偏差是相同的。但是,查看运行,第一个示例的运行长度方差要小得多,这意味着它更均匀,因此更好。

反正。我需要一个算法来生成给定长度和亮度的这些数组。如果可能,算法应找到一个最佳阵列 - 尽可能均匀的阵列。

它并不像听起来那么简单。说实话,我很想写一个比较所有可能性的蛮力算法并返回最佳。

我甚至考虑整合一个整数编程模型,但这对于这个问题似乎有些过分。

除非有人有更好的主意?

修改

事实证明,{ 1, 1, 1, 1, 0, 0, 0, 0 }{ 1, 0, 1, 0, 1, 0, 1, 0 }具有相同的运行差异,因此指标需要稍微复杂一点,因为第二个示例明显优于第一。 (对于更长的阵列长度,1可能会看到一些LED闪烁)。

2 个答案:

答案 0 :(得分:2)

好在这里。基本上,您要做的是最小化0或1的最大运行长度。一般的想法是分发我们可以做的。剩下的就是把它塞进前面。

N =总位数 n =#1s

void pwm(int N, int n, char* z = 0) {
    if (n == 0) {
        // degenerate case!
        for (int i = 0; i < N; i++) {
            printf("0");
        }
        printf("\n");
        return;
    } else if (n == N) {
        // degenerate case!
        for (int i = 0; i < N; i++) {
            printf("1");
        }
        printf("\n");
        return;
    }
    int m = N - n;
    int sep = m/n;
    int rem = m%n;
    int pre;
    int i,j;
    std::list<char> clist;

    if (z == 0) {
        // if more 1 than 0, then flip
        if (sep > 0) pwm(N,n,"01");
        else pwm(N,N-n,"10");
        return;
    }

    pre = sep/2;

    for (j = 0; j < pre; j++)
        clist.push_back(z[0]);
    if (rem > 0) {
        clist.push_back(z[0]);
        rem--;
    }

    for (i = 0; i < n; i++) {
        if (i!=0) {
            for (j = 0; j < sep; j++)
                clist.push_back(z[0]);
            if (rem > 0) {
                clist.push_back(z[0]);
                rem--;
            }
        }
        clist.push_back(z[1]);
    }

    for (j = 0; j < sep-pre; j++)
        clist.push_back(z[0]);


    // output the data so we can see
    char* res = new char[N+1];
    memset(res, ' ', N);
    res[N] = 0;
    char* u = res;
    for (std::list<char>::iterator cli = clist.begin(); cli != clist.end(); cli++) {
        (*u) = *cli;
        u++;
    }

    printf("%s\n", res);


    delete [] res;
}

这是N = 25的输出:

0000000000000000000000000
0000000000001000000000000
0000001000000000001000000
0000100000001000000010000
0001000001000001000001000
0010000100001000010000100
0010001000100010001000100
0010001000100010010010010
0010010010010010010010010
0100100100100100100101010
0100100100100101010101010
0100100101010101010101010
0101010101010101010101010
1010101010101010101010101
1011011010101010101010101
1011011011011010101010101
1011011011011011011010101
1101101101101101101101101
1101110111011101101101101
1101110111011101110111011
1101111011110111101111011
1110111110111110111110111
1111011111110111111101111
1111110111111111110111111
1111111111110111111111111

答案 1 :(得分:0)

假设你需要在n个点的网格中点亮k个点。您可以遵循以下概率方法。

remk=k
remn=n
for i in range(n):
    x[i] = 1 if rand()< remk*1.0/remn else 0
    remk-=x[i]
    remn-=1

x是包含照明布置的数组。