我必须找到一组字母组合,第一组中的第二个字母应该与第二组中的第一个字母相同等。
例如,该群组的解决方案:AA, CB, AC, BA, BD, DB
是这样的:CB, BD, DB, BA, AA, AC
到目前为止我有这个代码,它可以工作,但是如果有很多组,那么计算需要很长时间。我需要提高效率。
在输入文件中,输入
10
C D
B C
B B
B B
D B
B B
C A
A B
B D
D C
我的代码
#include <stdio.h>
#include <stdlib.h>
void permutation(char group[][2], int buffer, int sum) {
int i, j;
char temp;
if (buffer == sum && group[1][1] == group[sum][2]) {
for (i = 1; i < sum; i++)
if (group[i][2] != group[i+1][1]) break;
if (i == sum) {
FILE *output;
output = fopen("output.txt", "a");
for (j = 1; j <= sum; j++) {
fprintf(output, "%c %c\n", group[j][1], group[j][2]);
}
exit(1);
}
} else {
for (i = buffer; i <= sum; i++) {
temp = group[buffer][1];
group[buffer][1] = group[i][1];
group[i][1] = temp;
temp = group[buffer][2];
group[buffer][2] = group[i][2];
group[i][2] = temp;
permutation(group, buffer + 1, sum);
temp = group[buffer][1];
group[buffer][1] = group[i][1];
group[i][1] = temp;
temp = group[buffer][2];
group[buffer][2] = group[i][2];
group[i][2] = temp;
}
}
}
int main() {
FILE *input;
input = fopen("input.txt", "r");
int sum, i;
fscanf(input, "%d", &sum);
char group[sum][2];
for (i = 1; i <= sum; i++) {
fscanf(input, "%s", &group[i][1]);
fscanf(input, "%s", &group[i][2]);
}
permutation(group, 1, sum);
}
编辑所以我在我的程序中做了一些更改(感谢您的帮助,我对编程非常陌生,所以我很抱歉错误),我使用的排列没有更多,我只是寻找路径。它运作良好,但现在我的输入有100000组,它需要很多时间(大约2小时,我需要在1小时内完成最大值)。我可能不得不再次以其他方式做到这一点xD任何想法?
#include <stdio.h>
int find(char group[][2], int buffer, int sum, int path[]) {
int i, j;
for (i = 0; i < sum; i++) {
for (j = 0; j < buffer; j++)
if (path[j] == i)
break;
if (buffer == 0 ||
(group[path[buffer-1]][1] == group[i][0] && buffer == j)) {
printf("%d\n", buffer); // just for me to know what program is currently computing
path[buffer] = i;
find(group, buffer + 1, sum, path);
if (path[sum-1] != 0)
return;
}
}
}
int main() {
FILE *input = fopen("input.txt", "r");
if (input != NULL) {
int sum, i;
fscanf(input, "%d", &sum);
char group[sum][2];
int path[sum];
for (i = 0; i < sum; i++)
fscanf(input, " %c %c", &group[i][0], &group[i][1]);
for (i = 0; i < sum;i++)
path[i] = 0;
find(group, 0, sum, path);
FILE *output = fopen("output.txt", "a");
for (i = 0; i < sum; i++)
fprintf(output, "%c %c\n", group[path[i]][0], group[path[i]][1]);
} else
printf("Input file was not found.");
}
答案 0 :(得分:2)
在C数组中,索引从0开始,因此大小为N
的数组具有从0
到N-1
的有效索引。在上面的代码中,您正在访问数组group
越界,因为它的大小为2
(因此有效索引为0
和1
),但您正在尝试访问索引1
和2
。
要么改变:
char group[sum][2];
为:
char group[sum][3];
或使用索引0
/ 1
而不是1
/ 2
。
另请注意,您的代码缺少错误检查,例如致电fopen
。
答案 1 :(得分:1)
您的计划有几个问题:
fscanf
说明符解析输入%s
:这是不安全的,并且会为每个输入写入2个字节,写入超出每个子数组的末尾并超出最后一个末尾阵列。您已经知道如何解决这些问题,最好使用基于0的索引
您的算法非常无效,复杂性O(n!)
因为您枚举所有可能的排列并仅检查完整排列的有效性。您可以通过枚举已经验证其初始元素约束的排列来大幅提高性能。复杂性大大降低,仍然是二次的,但n
非常小。
以下是您的代码的修改版本:
#include <stdio.h>
int permutation(char group[][2], int buffer, int sum) {
if (buffer == sum)
return group[sum-1][1] == group[0][0];
for (int i = buffer; i < sum; i++) {
if (group[buffer-1][1] == group[i][0]) {
char temp = group[buffer][0];
group[buffer][0] = group[i][0];
group[i][0] = temp;
temp = group[buffer][1];
group[buffer][1] = group[i][1];
group[i][1] = temp;
if (permutation(group, buffer + 1, sum))
return 1;
temp = group[buffer][0];
group[buffer][0] = group[i][0];
group[i][0] = temp;
temp = group[buffer][1];
group[buffer][1] = group[i][1];
group[i][1] = temp;
}
}
return 0;
}
int main(void) {
FILE *input = fopen("input.txt", "r");
int sum, i;
if (input != NULL) {
if (fscanf(input, "%d", &sum) != 1 || sum <= 0) {
printf("invalid number of pairs\n");
fclose(input);
return 1;
}
char group[sum][2];
for (i = 0; i < sum; i++) {
if (fscanf(input, " %c %c", &group[i][0], &group[i][1]) != 2) {
printf("incorrect input for pair number %d\n", i);
fclose(input);
return 1;
}
}
fclose(input);
if (permutation(group, 1, sum)) {
FILE *output = fopen("output.txt", "a");
if (output == NULL) {
printf("cannot open output file\n");
return 2;
}
for (i = 0; i < sum; i++) {
fprintf(output, "%c %c\n", group[i][0], group[i][1]);
}
fclose(output);
return 0;
} else {
printf("complete path not found\n");
return 1;
}
}
printf("cannot open input file\n");
return 2;
}
我修改了代码的其他方面以提高效率和可重用性:
main
函数处理的,以保持一致性。上面的代码解决了我的笔记本电脑上n = 50 小于0.002秒的指定输入问题。它会打印F C C E E F F E E E E E E E E E E B B F F E E A A F F C C A A A A E E F F C C E E E E E E E E E E B B C C E E E E F F E E F F F F E E C C E E E E E E B B F F A A D D A A C C C C E E E E E E B B D D F
编辑我意识到,由于您正在寻找完整的封闭路径,因此您无需为第一对尝试不同的可能性。 main
可以使用permutation
而不是1
来呼叫0
,permutation
可以简化为buffer
永远不能0
。
答案 2 :(得分:0)
您的新代码存在一些问题:
find
被定义为返回int
,但您不返回任何内容。你确实没有测试你是否找到了一条完整的路径,完全依赖于至少有一条路径并且你已找到它的假设。used[sum]
效率低。find
功能。以下是改进版本:
#include <stdio.h>
int find(char group[][2], int buffer, int sum, int path[], unsigned char used[]) {
int i;
char last = group[path[buffer-1]][1];
if (buffer == sum)
return last == group[0][0];
for (i = 1; i < sum; i++) {
if (!used[i] && last == group[i][0]) {
path[buffer] = i;
used[i] = 1;
if (find(group, buffer + 1, sum, path, used))
return 1;
used[i] = 0;
}
}
return 0;
}
int main() {
FILE *input = fopen("input.txt", "r");
if (input != NULL) {
int sum = 0, i;
fscanf(input, "%d", &sum);
char group[sum][2];
int path[sum];
unsigned char used[sum];
for (i = 0; i < sum; i++)
fscanf(input, " %c %c", &group[i][0], &group[i][1]);
path[0] = 0; // always start at first element
used[0] = 1;
for (i = 1; i < sum; i++)
used[i] = 0;
if (find(group, 1, sum, path, used)) {
FILE *output = fopen("output.txt", "a");
for (i = 0; i < sum; i++)
fprintf(output, "%c %c\n", group[path[i]][0], group[path[i]][1]);
}
} else {
printf("Input file was not found.");
}
return 0;
}
编辑:我用你的大输入文件测试了这个新版本:它在我的笔记本电脑上崩溃了。具有permutation
功能的先前版本的工作方式类似于魅力,在0.060秒内生成完整路径。所以有一个完整的路径,这个find
函数有问题。
算法之间几乎没有差异:
permutation
使用更少的堆栈空间:单个自动数组大小为n * 2(200k),而3个自动数组的总大小为n*(sizeof(int) + 3)
(700k)。permutation
使用较少的变量,因此递归使用较少的堆栈空间,但两者都可能使用超过1 MB的堆栈空间来递归100000次。find
执行更多扫描,其中permutation
交换group
对并始终直接捕捉下一个。我在没有递归的情况下重新实现了find
,并最终得到它以产生完整的路径。它是一个不同的,计算需要更长的时间,3.5秒。
对于较大的输入文件,您绝对不应该使用递归,甚至应该使用malloc
从堆中分配数组。
这是非递归代码,使用堆内存:
#include <stdio.h>
#include <stdlib.h>
int find(const char group[][2], int sum, int path[]) {
path[0] = 0;
if (sum <= 1)
return group[0][1] == group[0][0];
unsigned char *used = calloc((size_t)sum, sizeof(*used));
for (int buffer = 1, i = 1;; i++) {
if (i == sum) {
--buffer;
if (buffer == 0) {
free(used);
return 0;
}
i = path[buffer];
used[i] = 0;
} else
if (!used[i] && group[path[buffer-1]][1] == group[i][0]) {
path[buffer] = i;
if (buffer == sum - 1) {
if (group[i][1] == group[0][0]) {
free(used);
return 1;
}
} else {
buffer++;
used[i] = 1;
i = 0;
}
}
}
}
int main() {
FILE *input = fopen("input.txt", "r");
if (input != NULL) {
int sum = 0, i;
fscanf(input, "%d", &sum);
char (*group)[2] = calloc((size_t)sum, sizeof(*group));
int *path = calloc((size_t)sum, sizeof(*path));
for (i = 0; i < sum; i++)
fscanf(input, " %c %c", &group[i][0], &group[i][1]);
if (find(group, sum, path)) {
FILE *output = fopen("output.txt", "a");
for (i = 0; i < sum; i++)
fprintf(output, "%c %c\n", group[path[i]][0], group[path[i]][1]);
}
} else {
printf("Input file was not found.");
}
return 0;
}