用于查找数组元素的 lcm 的 C 程序

C program for finding the lcm of array elements

提问人:jvkloc 提问时间:7/5/2023 最后编辑:jvkloc 更新时间:7/9/2023 访问量:180

问:

我正在尝试编写一个程序,该程序会迭代更新数组的最小数字,直到数组中的所有数字都相同(一种用于查找 lcm 的算法)。我的程序似乎最终陷入了无休止的循环。我的猜测是,这是由于在和/或中的某些东西导致的总是返回零。我想知道这是否与我不知道的一些 C 语言特性有关。一些有经验的 C 程序员可以帮助我解决这个问题吗?are_same()get_min_index()divisors[min_index] += divisors[min_index]

#include <stdio.h>

int are_same(int array[], int length) {
    for (int i = 1; i < length; i++) {
        if (array[0] != array[i]) {
            return 0;
        }
    }
    return 1;
}

int get_min_index(int array[], int length) {
    int min_value = array[0];
    int min_index = 0;
    for (int i = 1; i < length; i++) {
        if (array[i] < min_value) {
            min_value = array[i];
            min_index = i;
        }
    }
    return (min_index);
}

int main(void) {
    int divisors[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
    int length = sizeof(divisors) / sizeof(divisors[0]);
    int min_index;

    do {
        min_index = get_min_index(divisors, length);
        divisors[min_index] += divisors[min_index];
    } while (are_same(divisors, length) == 0);

    printf("lcm=%d", divisors[0]);
    return(0);
}
阵列 C 函数 LCM

评论

0赞 Barmar 7/5/2023
are_same()在我看来,两者看起来都很好。为什么你认为你应该把最小的元素加倍才能得到LCM?get_min_index()
0赞 Barmar 7/5/2023
尝试使用较小的示例,例如 ,并在循环的每次迭代后打印数组的内容。你会看到,重复的倍增永远不会让你接近LCM(即6)。{1, 2, 3}do
0赞 Barmar 7/5/2023
1, 2, 3 => 2, 2, 3 => 4, 2, 3 => 4, 4, 3 => 4, 4, 6 => 8, 4, 6 => 8, 8, 6 => 8, 8, 12
1赞 Ian Abbott 7/6/2023
与其加倍,不如添加原始除数。这意味着您需要一个有效的副本。我们称之为 ,初始化为与 相同的内容。在每次迭代中,do 和 update 并检查 .此外,您应该将除数更改为简单处理除数最初都相同的情况。divisors[]working[]divisors[]min_index = get_min_index(working, length);working[min_index] += divisors[min_index];are_same(working, length)do whilewhile
0赞 jvkloc 7/6/2023
@Barmar我不认为加倍会得到结果。但我确实写了。我的想法是伊恩·阿博特(Ian Abbot)所写的。

答:

0赞 prachi ka.patel 7/6/2023 #1

你好,我不认为语言有问题 或编译器,尽管我有您的代码的增强版本。你可以试试 并检查它是否适合您。

#include <stdio.h>

int are_same(int array[], int length) {
    for (int i = 1; i < length; i++) {
        if (array[0] != array[i]) {
            return 0;
        }
    }
    return 1;
}

int get_min_index(int array[], int length) {
    int min_value = array[0];
    int min_index = 0;
    for (int i = 1; i < length; i++) {
        if (array[i] < min_value) {
            min_value = array[i];
            min_index = i;
        }
    }
    return min_index;
}

int calculate_lcm(int array[], int length) {
    int lcm = array[0];
    for (int i = 1; i < length; i++) {
        int a = lcm;
        int b = array[i];
        // Calculate the greatest common divisor (GCD) using Euclid's algorithm
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        // Calculate the least common multiple (LCM)
        lcm = (lcm * array[i]) / a;
    }
    return lcm;
}

int main(void) {
    int divisors[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
    int length = sizeof(divisors) / sizeof(divisors[0]);

    int lcm = calculate_lcm(divisors, length);

    printf("LCM = %d\n", lcm);
    return 0;
}

评论

0赞 jvkloc 7/6/2023
嗨,普拉奇!你的代码给出了错误的答案。18044195
0赞 jvkloc 7/6/2023
使用我更正的代码可以得出正确答案,它是 .也可以用欧几里得算法来获得它。但是你的代码有问题。232792560
1赞 Ted Lyngmo 7/7/2023 #2

您的实现很好,可以完成工作。are_same

该算法找到最小的并添加其相应的除数,然后搜索最小的并重复执行相同的操作,该算法有一个错误,因为您将值加倍而不是将除数添加到其中。

即使正确实现,也可能需要很长时间才能完成,因为您搜索整个数组以便只更新一次一个值。相反,如果您可以将除数乘以 x 的乘积相加,使其至少与数组中的最大值一样大,则速度会更快。

然后稍作修改

  • 搜索数组中的最大值。
  • 用相应除数的倍数增加所有条目,使该值至少与最大值一样大。array
  • 重复上述步骤,直到所有值都相等。

在这个例子中,我不关心最大的索引,所以这只返回最大的

int largest_value(int arr[], size_t length) {
    int l = arr[0];
    for(size_t i = 1; i < length; ++i) {
        if(arr[i] > l) l = arr[i];
    }
    return l;
}

函数需要复制传入数组(以修复当前实现中的错误)并实现上述算法:calculate_lcm

int calculate_lcm(const int array[], size_t length) {
    // copy array in to a "working array", wa:
    int* wa = malloc(length * sizeof *wa);
    if(!wa) exit(1);
    memcpy(wa, array, length * sizeof *wa);

    while(!are_same(wa, length)) {
        // find the largest value in wa:
        int large = largest_value(wa, length);

        for(size_t i = 0; i < length; ++i) {
            // make wa[i] at least as big as `large`
            wa[i] += ((large - wa[i] + array[i] - 1) / array[i]) * array[i];
        }
    }

    int lcm = wa[0];
    free(wa);
    return lcm;
}

演示

您也可以跳过调用,只检查自上次迭代以来最大值是否发生了变化。如果没有,则所有数字都相同:all_same

int calculate_lcm(const int array[], size_t length) {
    int* wa = malloc(length * sizeof *wa);
    if (!wa) exit(1);
    memcpy(wa, array, length * sizeof *wa);

    for(int largest = array[0];;) {
        int newlargest = largest_value(wa, length);
        if(newlargest == largest) break;
        largest = newlargest;

        for (size_t i = 0; i < length; ++i) {
            wa[i] += ((largest - wa[i] + array[i] - 1) / array[i]) * array[i];
        }
    }

    int lcm = wa[0];
    free(wa);
    return lcm;
}

演示

评论

0赞 jvkloc 7/8/2023
每次在函数中创建和销毁数组真的比像我的原始代码那样一直拥有它的单个副本更好吗?我想是,如果阵列很大并且占用空间?我会接受这个答案@Ian除非阿博特在几天后出现并写下他的评论。他首先指出了我的逻辑错误。
0赞 Ted Lyngmo 7/8/2023
@jvkloc“每次”——复制一次,是的,它更好,因为它有效,而你的方法无效。如果被复制的对象不是一个巨大的对象,那么使用乘数数组而不是实际值会更可取,但是,它仍然需要以某种方式分配(使用或 VLA)内存的步骤,同时保持原始除数数组不变。intmallocmalloc
0赞 jvkloc 7/8/2023
哦,对不起。我写的更新有点不同。array_update,are_same 和 largest_value 在 main 的 while 循环中被调用。
0赞 Ted Lyngmo 7/8/2023
@jvkloc 什么是?此外,您的问题中没有功能。这是我小的实现调整的一部分,以使其在几秒钟而不是几小时内完成。array_updatelargest_value
0赞 jvkloc 7/8/2023
我写的版本,我使用你的建议与你的例子略有不同。我没有真正检查你的代码并根据这个想法编写它。此外,第一条评论中的问题是关于在每个函数调用中创建/销毁数组。与一直只有一个副本可用相比,这似乎有点浪费。