簡體   English   中英

查找組和字母的組合

[英]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的數組具有從0N-1有效索引。 在上面的代碼要訪問的數組group出界,因為它具有大小2 (因此有效索引是01 ),但你正在試圖訪問索引12

要么改變:

char group[sum][2];

至:

char group[sum][3];

或使用指數0 / 1 ,而不是1 / 2

另請注意,您的代碼缺少錯誤檢查,例如在調用fopen

你的程序有幾個問題:

  • 您使用基於1的索引,這會導致混淆並導致引用超出其定義目的的數組和子數組。
  • 你使用%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;
}

我修改了代碼的其他方面以提高效率和可重用性:

  • 檢查輸入的有效性。
  • 遞歸函數停止並在找到完整路徑時返回1。 這允許程序繼續,無論它是否找到路徑。
  • 輸出從main函數處理以保持一致性。

上面的代碼解決了筆記本電腦上n = 50的指定輸入問題, 小於0.002秒 它打印FCCEEFFEEEEEEEEEEBBFF EEAAFFCCAAAAEEFFCCEEE EEEEEEEBBCCEEEEFFEEFF FFEECCEEEEEEBBFFAADDA ACCCCEEEEEEBBDDF

編輯我意識到,既然你正在尋找一個完整的封閉路徑,你不需要為第一對嘗試不同的可能性。 main可以用1代替0來調用permutation ,並且可以簡化permutation因為buffer永遠不能為0

您的新代碼存在一些問題:

  • find被定義為返回int ,但是你什么也沒有返回。 你確實不測試你是否找到了完整的路徑,完全依賴於至少有一個並且你已經找到它的假設。
  • 您不測試路徑關閉。 您可能偶然發現一條封閉的路徑,但您也可能會產生一條未封閉的路徑。
  • 使用2個循環來查找未使用的對比使用使用的臨時數組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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM