---
# System prepended metadata

title: Euler Project 14

---

# Euler Project 14
![](https://hackmd.io/_uploads/rkEERnhP3.png)

## linked list
1. 在 collatzLength 函式中，使用 while 迴圈來計算 Collatz 數列的長度。迴圈的條件是當 n 不等於 1 時繼續執行。

2. 在迴圈內部，首先檢查是否已經計算過數字 n 的 Collatz 數列長度。如果 n < LIMIT 且 lens[n] 不是 NULL，則表示已經計算過，我們可以使用儲存在 lens[n] 的節點值來更新目前的長度 len。並減去 1 以獲得正確的長度。

3. 如果 lens[n] 是 NULL 或 n >= LIMIT，表示我們還未計算過該數字的 Collatz 數列長度。在這種情況下，我們根據 Collatz 數列的規則更新 n 的值並將 len 的計數增加 1。
```javascript=
#define LIMIT 1000000

int collatzLength(long n, Node** lens) {
    int len = 1;

    while (n != 1) {
        if (n < LIMIT && lens[n] != NULL) {
            len += lens[n]->value - 1;
            break;
        }

        if (n % 2 == 0)
            n /= 2;
        else
            n = n * 3 + 1;

        len++;
    }

    return len;
}
```
4. 程式碼中的 freeLinkedList 函式，用於釋放連結串列所佔用的記憶體。使用迴圈遍歷串列並釋放每個節點。
```javascript=
void freeLinkedList(Node* head) {
    while (head != NULL) {
        Node* temp = head;
        head = head->next;
        free(temp);
    }
}
```
5. 在 main 函式中，使用 malloc 分配一個 Node* 指標的陣列，大小為 LIMIT。然後，使用迴圈將每個指標初始化為 NULL。
```javascript=
Node** lens = (Node**)malloc(LIMIT * sizeof(Node*));
    for (int i = 0; i < LIMIT; ++i)
        lens[i] = NULL;
```
6. 為數字 1 創建一個節點。在 lens[1] 中使用 malloc 分配一個新的 Node，並將 value 設置為 1，表示數字 1 的 Collatz 數列長度。next 指標設置為 NULL。
```javascript=
lens[1] = (Node*)malloc(sizeof(Node));
lens[1]->value = 1;
lens[1]->next = NULL;
```
7. 在主迴圈中，我們使用迴圈從 2 開始遍歷到 LIMIT - 1。對於每個數字 i，我們調用 collatzLength 函式來計算 Collatz 數列的長度。

8. 如果計算得到的長度 len 大於目前的最大長度 maxlen，則更新 maxlen 和 maxi 的值。

9. 在計算完 Collatz 數列長度後，我們檢查 i 是否小於 LIMIT，如果是的話，則使用 malloc 分配一個新的節點並將其添加到 lens 陣列的相應位置。
```javascript=
int maxi = 1, maxlen = 1;

for (int i = 2; i < LIMIT; ++i) {
    int len = collatzLength(i, lens);

    if (len > maxlen) {
        maxlen = len;
        maxi = i;
    }

    if (i < LIMIT) {
        Node* newNode = (Node*)malloc(sizeof(Node));
        newNode->value = len;
        newNode->next = NULL;
        lens[i] = newNode;
    }
}
```
10. 最後，我們使用迴圈遍歷 lens 陣列，釋放每個數字的連結串列所佔用的記憶體。然後，使用 free 釋放 lens 陣列本身所佔用的記憶體。
```javascript=
for (int i = 1; i < LIMIT; ++i)
        freeLinkedList(lens[i]);
    free(lens);
```
### 為甚麼要開兩層指標?
在程式碼中使用兩層指標 Node** lens 是為了在 collatzLength 函式中能夠修改 lens 陣列中的節點指標。

原始的 lens 陣列是一個 Node* 指標的陣列，每個指標指向一個連結串列的節點。當我們在 collatzLength 函式中計算 Collatz 數列的長度時，需要能夠存取和修改這個連結串列。然而，如果只使用單層指標，則在函式內部修改 lens 陣列中的指標只會影響到函式內部的複本，不會影響到 main 函式中的 lens 陣列。

因此，我們使用兩層指標 Node** lens，其中第一層指標 Node** 指向 lens 陣列本身，第二層指標 Node* 指向連結串列的節點。這樣，在 collatzLength 函式中修改 lens 陣列的指標時，可以直接影響到 main 函式中的 lens 陣列。

總結而言，使用兩層指標 Node** lens 可以在 collatzLength 函式中修改 lens 陣列的內容，以便儲存每個數字的 Collatz 數列的長度。這樣就能夠在主函式中使用這些資訊，進一步處理和顯示結果。
## 最終成果
```javascript=
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    long value;
    struct Node* next;
} Node;

#define LIMIT 1000000

int collatzLength(long n, Node** lens) {
    int len = 1;

    while (n != 1) {
        if (n < LIMIT && lens[n] != NULL) {
            len += lens[n]->value - 1;
            break;
        }

        if (n % 2 == 0)
            n /= 2;
        else
            n = n * 3 + 1;

        len++;
    }

    return len;
}

void freeLinkedList(Node* head) {
    while (head != NULL) {
        Node* temp = head;
        head = head->next;
        free(temp);
    }
}

int main() {
    Node** lens = (Node**)malloc(LIMIT * sizeof(Node*));
    for (int i = 0; i < LIMIT; ++i)
        lens[i] = NULL;

    lens[1] = (Node*)malloc(sizeof(Node));
    lens[1]->value = 1;
    lens[1]->next = NULL;

    int maxi = 1, maxlen = 1;

    for (int i = 2; i < LIMIT; ++i) {
        int len = collatzLength(i, lens);

        if (len > maxlen) {
            maxlen = len;
            maxi = i;
        }

        if (i < LIMIT) {
            Node* newNode = (Node*)malloc(sizeof(Node));
            newNode->value = len;
            newNode->next = NULL;
            lens[i] = newNode;
        }
    }

    printf("%d: %d\n", maxi, maxlen);

    for (int i = 1; i < LIMIT; ++i)
        freeLinkedList(lens[i]);
    free(lens);

    return 0;
}
```


## 遞迴版
1. collatzLength 函式用於計算給定數字的考拉茲序列長度。它接受一個數字 n 和一個保存已計算長度的陣列 lens，並回傳數字 n 的序列長度。
```javascript=
int collatzLength(long n, int* lens)
```

2. 如果 n 的值為 1，表示序列已結束，因此回傳長度 1。

3. 如果 n 的值小於 LIMIT（1000000）並且 lens[n] 不為 0，表示我們之前已計算過數字 n 的序列長度，可以直接從陣列 lens 中取得並返回。

```javascript=
if (n == 1)
    return 1;
if (n < LIMIT && lens[n] != 0)
    return lens[n];
```

4. 如果以上條件不滿足，我們需要根據考拉茲猜想的規則計算序列長度。根據規則：

    如果 n 是偶數，則將其除以 2
    如果 n 是奇數，則將其乘以 3 並加 1
    遞迴調用 collatzLength 函式，傳入新的數字並累加 1。
```javascript=
if (n % 2 == 0)
    len = collatzLength(n / 2, lens) + 1;
else
    len = collatzLength(n * 3 + 1, lens) + 1;
```

5. 如果 n 小於 LIMIT，我們將計算出的長度存儲在 lens 陣列中。
```javascript=
if (n < LIMIT)
    lens[n] = len;
```
## 最終成果
```javascript=
#include <stdio.h>

#define LIMIT 1000000

int collatzLength(long n, int* lens) {
    if (n == 1)
        return 1;
    if (n < LIMIT && lens[n] != 0)
        return lens[n];

    int len = 1;
    
    if (n % 2 == 0)
        len = collatzLength(n / 2, lens) + 1;
    else
        len = collatzLength(n * 3 + 1, lens) + 1;

    if (n < LIMIT)
        lens[n] = len;
    
    return len;
}

int main() {
    int lens[LIMIT] = {0}; // 初始化0
    
    lens[1] = 1; // 1的長度就是1

    int maxi = 1, maxlen = 1; // 初始化

    for (int i = 2; i < LIMIT; ++i) {
        int len = collatzLength(i, lens);

        if (len > maxlen) {
            maxlen = len;
            maxi = i;
        }
    }

    printf("%d: %d\n", maxi, maxlen);
    return 0;
}

```
## 時間複雜度:
時間複雜度大概為 O(n log n)，其中 n 是 LIMIT 的值，也就是 1000000。


