[英]Find combination of groups and letters
我必須找到一組字母組合,第一組中的第二個字母應該與第二組中的第一個字母相同。
例如,該組的解決方案: 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.");
}
在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
。
你的程序有幾個問題:
%s
說明符用fscanf
解析輸入:這是不安全的,並且會為每個輸入寫入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秒 。 它打印FCCEEFFEEEEEEEEEEBBFF EEAAFFCCAAAAEEFFCCEEE EEEEEEEBBCCEEEEFFEEFF FFEECCEEEEEEBBFFAADDA ACCCCEEEEEEBBDDF
編輯我意識到,既然你正在尋找一個完整的封閉路徑,你不需要為第一對嘗試不同的可能性。 main
可以用1
代替0
來調用permutation
,並且可以簡化permutation
因為buffer
永遠不能為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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.