C函数使用加法和减法获取数组的所有总和

Posted

技术标签:

【中文标题】C函数使用加法和减法获取数组的所有总和【英文标题】:C function to get all sums of an array using addition and subtraction 【发布时间】:2022-01-08 10:07:38 【问题描述】:

使用加法和减法来获得数组中所有 N 个数字的总和的最佳方法是什么(在 C 中)?

例如(N = 3):

arr[] = [30, 14, 2]

results:
-30-14-2 = -46
-30-14+2 = -42
-30+14-2 = -18
-30+14+2 = -14
 30-14-2 = 14
 30-14+2 = 18
 30+14-2 = 42
 30+14+2 = 46

可以看出,有2^N个解。

我还注意到,加法和减法符号的交替方式与二进制计数 (000 001 ... 110 111) 相同,这可能很有用。

也许递归方法是最好的,但我发现递归思考非常困难。

因此,我希望有人能向我解释解决这个问题的最佳策略是什么。

—————————

编辑:

我有一个可用的 Python 代码,但这使用了集合 set(),这在 C 中不可用。(arr 是一个包含所有数字的数组。)

out = set()
out.add(0)
for i in range(0, len(arr)):
    tmp = set()
    for j in out:
        tmp.add(j + arr[i])
        tmp.add(j - arr[i])
    out = tmp
print(out)

—————————

编辑:

通过用数组替换集合并进行一些小的更改,我得到了它的工作。感谢所有评论的人!

【问题讨论】:

这是一个观点:你不应该递归地这样做,而只是在两个循环中......一个循环操作(因为它们可以用累加器完成)和第二个循环值,您将获得结果而不会遇到内存问题。 建议:1) 我会考虑将此作为permutations 问题的重点。 2)我不知道递归是否“更好”,但是你可以用递归做任何事情,你也可以用循环做。 3)我不确定是否一定有 2^N 个“解决方案”。例如,“a+b”==“b+a”(它们是“相同的”);但是“a-b”“b-a”(它们是两种不同的“解决方案”)。 不用集合,只用一个数组。性能在这里有多重要? 如果您有解决方案,请考虑发布self answer 以帮助其他有同样问题的人。 有趣的是,结果的下半部分是上半部分的 (-1)。看起来你只需要做一半的数学,然后(-1)整个解决方案。 【参考方案1】:

在这种情况下,递归似乎比数据结构更可取,因为问题的指数性质意味着您可以实际测试的任何 n 值都没有堆栈溢出的风险,但您可以利用堆栈的优势隐式存储功能。

可以使用索引i 非常直接地编写递归逻辑。基本情况是i >= n;打印您累积的总和。否则,从总和中减去ith 元素并在没有i 的数字列表的其余部分上递归。在第二个分支中,将ith 元素添加到总和中,并在没有i 的数字列表的其余部分上递归。如果 i 如 cmets 中指出的那样,您也可以使用指针将元素移出数组。

请注意,此过程将包含重复的结果,因此如果您想消除这些重复的结果,您最初的设定想法值得追求。

在函数中混合 IO 和逻辑通常是糟糕的设计,但为了简单起见,我将在这里打破这条规则:

#include <stdio.h>

void print_possible_sums(int nums_len, int *nums, int sum, int i) 
    if (i >= nums_len) 
        printf("%d\n", sum);
    
    else 
        print_possible_sums(nums_len, nums, sum - nums[i], i + 1);
        print_possible_sums(nums_len, nums, sum + nums[i], i + 1);
    


int main() 
    int arr[] = 30, 14, 2;
    print_possible_sums(sizeof(arr) / sizeof(arr[0]), arr, 0, 0);
    return 0;

输出:

-46
-42
-18
-14
14
18
42
46

【讨论】:

也许我应该感到受宠若惊,但你可以提到你的inspiration? @EOF 我不确定我是否按照您的提示进行操作,但是指针的想法是一个不错的改进,感谢分享。 真是不可思议,我们的编程风格如此相似。 你是在暗示我复制它吗?如果是这样,情况并非如此,我不确定我是如何找到该链接的。问题很简单,所以解决方案也很直接,所以我不确定会有多大的偏差空间。【参考方案2】:

我已经制作了一个算法来解决这个问题。也许我写的不是尽可能高效(所以也许可以改进),但至少它是有效的。

让我知道是否可以。

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define MAX 4
void cycle(int arr[MAX]);
int toRepeat(int arr[MAX], int s);

int main()
    int arr[MAX] = 1, 2, 3, 4;
    cycle(arr);


void cycle(int arr[MAX])
    int a, ris;
    a = pow(2, MAX);
    for (int i= 0; i < a; i++)
        ris = toRepeat(arr, i);
        printf(" = %3d \n", ris);
    


int toRepeat(int arr[MAX], int s)
    int pos = 0;
    int ris = 0;
    int a;
    bool flag;
    a = pow(2, MAX);
    if (s < a/2) 
        ris += arr[pos];
        printf("+%d", arr[pos]);
     else 
        ris -= arr[pos];
        printf("-%d", arr[pos]);
    
    pos++;
    
    for (int i= 2; i < a; i = i * 2)
        for (int j= 0; j < i/2; j++) 
            if (s%i == j) 
                ris += arr[pos];
                printf("+%d", arr[pos]);
                pos++;
                flag = true;
                break;
            
        
        if (!flag)
            ris -= arr[pos];
            printf("-%d", arr[pos]);
            pos++;
        
        flag = false;
    
    return ris;

输出:

+1+2+3+4 =  10
+1-2+3+4 =   6
+1+2-3+4 =   4
+1-2-3+4 =   0
+1+2+3-4 =   2
+1-2+3-4 =  -2
+1+2-3-4 =  -4
+1-2-3-4 =  -8
-1+2+3+4 =   8
-1-2+3+4 =   4
-1+2-3+4 =   2
-1-2-3+4 =  -2
-1+2+3-4 =   0
-1-2+3-4 =  -4
-1+2-3-4 =  -6
-1-2-3-4 = -10

【讨论】:

为了微笑,我用原始数据集编译了你的代码(我需要做一些小改动),并确认它给出了预期的结果:+30+14+2 = 46 +30-14+2 = 18 +30+14-2 = 42 +30-14-2 = 14 -30+14+2 = -14 -30-14+2 = -42 -30+14-2 = -18 -30-14-2 = -46【参考方案3】:

我也是,我也是!

使用bool negative[3 + 1] = 0; 保留一个数组。在每一步计算array[i] * (negative[i]?-1:1) 的总和。每一步都会以负数递增下一个值,就好像它是一个二进制数一样。当最高元素 negative[3] 为真时,循环结束 - 所以我们想要通过所有可能性。

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>

int calc_sum(size_t len, const int vals[len], const bool negative[len]) 
    int sum = 0;
    for (size_t i = 0; i < len; ++i) 
        sum += vals[i] * (negative[i] ? -1 : 1);
    
    return sum;


bool *sequence_init(size_t len) 
    // error checking omitted
    return calloc(len + 1, sizeof(bool));


bool sequence_continue(size_t len, bool t[len + 1]) 
    assert(t);
    const bool go_on = !t[len];
    if (!go_on) 
        // automatically free on loop end
        free(t);
    
    return go_on;


void sequence_next(size_t len, bool t[len + 1]) 
    assert(t);
    for (size_t i = 0; i < len + 1; ++i) 
        if (!t[i]) 
            t[i] = 1;
            break;
         else 
            t[i] = 0;
        
    


int main() 
    const int vals[3] =  30, 14, 2 ;
    const size_t len = 3;
    for(
        bool *sequence = sequence_init(len);
        sequence_continue(len, sequence);
        sequence_next(len, sequence)
    ) 
        for (size_t i = 0; i < len; ++i) 
            printf("%2d*%d %c ",
                sequence[i]?-1:1,
                vals[i],
                i + 1 != len ? '+' : '='
            );
        
        const int sum = calc_sum(len, vals, sequence);
        printf("%d\n", sum);
    

代码输出:

 1*30 +  1*14 +  1*2 = 46
-1*30 +  1*14 +  1*2 = -14
 1*30 + -1*14 +  1*2 = 18
-1*30 + -1*14 +  1*2 = -42
 1*30 +  1*14 + -1*2 = 42
-1*30 +  1*14 + -1*2 = -18
 1*30 + -1*14 + -1*2 = 14
-1*30 + -1*14 + -1*2 = -46

【讨论】:

以上是关于C函数使用加法和减法获取数组的所有总和的主要内容,如果未能解决你的问题,请参考以下文章

第七章 集合运算 7-1 表的加减法

Excel 2007减法得数变成了5E-05怎么办?

“时钟算术”/全等数学中的一个加法/减法函数?

复数类重载加法减法和乘法运算符

C语言中的颜色和边框的函数怎么来运用···

通过连续自然数的加法或减法获得一个数