Tree Search(樹狀搜尋)

一、什麼是 Tree Search(樹狀搜尋)? 在人工智慧(AI)與演算法中,許多問題都可以表示成一棵樹(圖一): 起點(A) / | \ B C D /|\ | / \ E F G H I J 每個節點(Node)代表一種狀態(State)。 例如: 迷宮中的位置 棋局的盤面 路徑規劃中的城市 遊戲中的決策 搜尋演算法的目的: 從起點找到目標節點(Goal Node) 二、Breadth First Search (BFS) 核心思想 先搜尋離起點最近的節點。 一層一層往外擴展。 Level 0: A Level 1: B C D Level 2: E F G H I J 搜尋順序: A B C D E F G H I J 圖一結果: A → B → C → D → E → F → G → H → I → J 使用資料結構 Queue(佇列) FIFO: First In First Out 先進先出 例如: Queue: A 取出A 加入B,C,D Queue: B,C,D BFS特性 優點 如果邊權重相同: BFS一定找到最短路徑。 缺點 需要大量記憶體。 假設每個節點有10個子節點: 深度5: 10^5 = 100000 需要保存很多節點。 時間複雜度 O(V + E) V = Vertex(節點數) E = Edge(邊數) 三、Depth First Search (DFS) 核心思想 一路往下走到底。 不能走才回頭。 A | B | E 然後: A | B | F 搜尋順序 圖一結果: A B E F G C H D I J 使用資料結構 Stack(堆疊) LIFO Last In First Out 後進先出 例如: push(B) push(C) push(D) pop() => D DFS特性 優點 記憶體需求小。 只需保存: 目前路徑 即可。 缺點 可能找到很差的解。 例如: A ├── Goal └── 巨大子樹 DFS可能先跑完整個巨大子樹。 時間複雜度 O(V+E)...

怪怪的程式語言名稱:Brainfuck

最近在 ptt Gossiping 版的jserv 的一篇回文
https://www.ptt.cc/bbs/Gossiping/M.1555612071.A.14C.html

得知 Brainfuck 也是個程式語言,於是小編自己也來寫一個 Brainfuck interpreter。先來了解 Brainfuck 的指令吧。

Brainfuck指令說明
============底下文字從 https://en.wikipedia.org/wiki/Brainfuck 節錄出來:============
CharacterMeaning
>increment the data pointer (to point to the next cell to the right).
<decrement the data pointer (to point to the next cell to the left).
+increment (increase by one) the byte at the data pointer.
-decrement (decrease by one) the byte at the data pointer.
.output the byte at the data pointer.
,accept one byte of input, storing its value in the byte at the data pointer.
[if the byte at the data pointer is zero, then instead of moving the instruction pointer forward to the next command, jump it forward to the command after the matching ] command.
]if the byte at the data pointer is nonzero, then instead of moving the instruction pointer forward to the next command, jump it back to the command after the matching [ command.
brainfuck指令與C語言對照表:
brainfuck commandC equivalent
(Program Start)char array[30000] = {0};
char *ptr=array;
>++ptr;
<--ptr;
+++*ptr;
---*ptr;
.putchar(*ptr);
,*ptr=getchar();
[while (*ptr) {
]}
============以上文字從 https://en.wikipedia.org/wiki/Brainfuck 節錄出來:============
其中,[ 與 ] 這兩個指令需要注意有巢狀的情況,而測試的 Brainfuck 程式碼如下:

hello.bf (https://github.com/pablojorge/brainfuck/blob/master/programs/hello.bf)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
+++++ +++++             initialize counter (cell #0) to 10
[                       use loop to set the next four cells to 70/100/30/10
    > +++++ ++              add  7 to cell #1
    > +++++ +++++           add 10 to cell #2 
    > +++                   add  3 to cell #3
    > +                     add  1 to cell #4
    <<<< -                  decrement counter (cell #0)
]                   
> ++ .                  print 'H'
> + .                   print 'e'
+++++ ++ .              print 'l'
.                       print 'l'
+++ .                   print 'o'
> ++ .                  print ' '
<< +++++ +++++ +++++ .  print 'W'
> .                     print 'o'
+++ .                   print 'r'
----- - .               print 'l'
----- --- .             print 'd'
> + .                   print '!'
> .                     print '\n'

費式數列 fibonacci.bf (https://github.com/pablojorge/brainfuck/blob/master/programs/fibonacci.bf)
1
2
3
4
5
6
7
8
9
>++++++++++>+>+[
    [+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[
        [-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-
            [>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>>
    ]<<<
]
This program doesn't terminate; you will have to kill it.
Daniel B Cristofani (cristofdathevanetdotcom)
http://www.hevanet.com/cristofd/brainfuck/

圓周率 pi.bf (https://copy.sh/brainfuck/prog/yapi.b)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[ yet another pi calculation program in bf

  Just like for pi16.b the accuracy of the result depends on the cellsize:
 
   - using  8 bit cells causes an overflow after 4 digits
   - using 16 bit cells causes an overflow after 537 digits
   - using 32 bit cells causes an overflow after several millions of digits
  
  It's about ~38 times shorter than pi16.b, ~364 times faster and works with
  not-wrapping (bignum) implementations. 
 
  by Felix Nawothnig (felix.nawothnig@t-online.de) ]

>  +++++ +++++ +++++ (15 digits)

[<+>>>>>>>>++++++++++<<<<<<<-]>+++++[<+++++++++>-]+>>>>>>+[<<+++[>>[-<]<[>]<-]>>
[>+>]<[<]>]>[[->>>>+<<<<]>>>+++>-]<[<<<<]<<<<<<<<+[->>>>>>>>>>>>[<+[->>>>+<<<<]>
>>>>]<<<<[>>>>>[<<<<+>>>>-]<<<<<-[<<++++++++++>>-]>>>[<<[<+<<+>>>-]<[>+<-]<++<<+
>>>>>>-]<<[-]<<-<[->>+<-[>>>]>[[<+>-]>+>>]<<<<<]>[-]>+<<<-[>>+<<-]<]<<<<+>>>>>>>
>[-]>[<<<+>>>-]<<++++++++++<[->>+<-[>>>]>[[<+>-]>+>>]<<<<<]>[-]>+>[<<+<+>>>-]<<<
<+<+>>[-[-[-[-[-[-[-[-[-<->[-<+<->>]]]]]]]]]]<[+++++[<<<++++++++<++++++++>>>>-]<
<<<+<->>>>[>+<<<+++++++++<->>>-]<<<<<[>>+<<-]+<[->-<]>[>>.<<<<[+.[-]]>>-]>[>>.<<
-]>[-]>[-]>>>[>>[<<<<<<<<+>>>>>>>>-]<<-]]>>[-]<<<[-]<<<<<<<<]++++++++++.

自然數 e.bf (http://www.hevanet.com/cristofd/brainfuck/e.b)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[e.b -- compute e
(c) 2016 Daniel B. Cristofani
http://brainfuck.org/]

>>>>++>+>++>+>>++<+[  
  [>[>>[>>>>]<<<<[[>>>>+<<<<-]<<<<]>>>>>>]+<]>-
  >>--[+[+++<<<<--]++>>>>--]+[>>>>]<<<<[<<+<+<]<<[
    >>>>>>[[<<<<+>>>>-]>>>>]<<<<<<<<[<<<<]
    >>-[<<+>>-]+<<[->>>>[-[+>>>>-]-<<-[>>>>-]++>>+[-<<<<+]+>>>>]<<<<[<<<<]]
    >[-[<+>-]]+<[->>>>[-[+>>>>-]-<<<-[>>>>-]++>>>+[-<<<<+]+>>>>]<<<<[<<<<]]<<
  ]>>>+[>>>>]-[+<<<<--]++[<<<<]>>>+[
    >-[
      >>[--[++>>+>>--]-<[-[-[+++<<<<-]+>>>>-]]++>+[-<<<<+]++>>+>>]
      <<[>[<-<<<]+<]>->>>
    ]+>[>>>>]-[+<<<<--]++<[
      [>>>>]<<<<[
        -[+>[<->-]++<[[>-<-]++[<<<<]+>>+>>-]++<<<<-]
        >-[+[<+[<<<<]>]<+>]+<[->->>>[-]]+<<<<
      ]
    ]>[<<<<]>[
      -[
        -[
          +++++[>++++++++<-]>-.>>>-[<<<----.<]<[<<]>>[-]>->>+[
            [>>>>]+[-[->>>>+>>>>>>>>-[-[+++<<<<[-]]+>>>>-]++[<<<<]]+<<<<]>>>
          ]+<+<<
        ]>[
          -[
            ->[--[++>>>>--]->[-[-[+++<<<<-]+>>>>-]]++<+[-<<<<+]++>>>>]
            <<<<[>[<<<<]+<]>->>
          ]<
        ]>>>>[--[++>>>>--]-<--[+++>>>>--]+>+[-<<<<+]++>>>>]<<<<<[<<<<]<
      ]>[>+<<++<]<
    ]>[+>[--[++>>>>--]->--[+++>>>>--]+<+[-<<<<+]++>>>>]<<<[<<<<]]>>
  ]>
]

This program computes the transcendental number e, in decimal. Because this is
infinitely long, this program doesn't terminate on its own; you will have to
kill it. The fact that it doesn't output any linefeeds may also give certain
implementations trouble, including some of mine.

C語言程式碼:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <stdlib.h>
#include <stdio.h>

static int cells[30000] = {0};
unsigned int dp = 0;      // data pointer

void bfExe(char *cmds)
{
    int i, nestedLoop, curCmd;

    for(i = 0; cmds[i] != 0; i++)
    {
        curCmd = cmds[i];
        switch(curCmd)
        {
        case '>':
            ++dp;
            break;
        case '<':
            --dp;
            break;
        case '+':
            ++cells[dp];
            break;
        case '-':
            --cells[dp];
            break;
        case '.':
            putchar(cells[dp]);
            break;
        case ',':
            cells[dp] = getchar();
            break;
        case '[':
            if(!cells[dp])
            {
                nestedLoop = 1;
                while(nestedLoop)
                {
                    i++;
                    if(cmds[i] == '[') nestedLoop++;
                    if(cmds[i] == ']') nestedLoop--;
                }
            }
            break;
        case ']':
            if(cells[dp])
            {
                nestedLoop = 1;
                while(nestedLoop)
                {
                    i--;
                    if(cmds[i] == ']') nestedLoop++;
                    if(cmds[i] == '[') nestedLoop--;
                }
            }
            break;
        }
    }
}

int main(int argc, char* argv[]) {
    if(argc < 2) exit(1);
    FILE *f = fopen(argv[1], "r");
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    fseek(f, 0, SEEK_SET);

    int *bfCmd = malloc(fsize + 1);
    fread(bfCmd, 1, fsize, f);
    fclose(f);
    bfCmd[fsize] = 0;

    bfExe(bfCmd);
    return 0;
}

而在 stackoverflow 上有高手寫了底下的C程式碼(https://stackoverflow.com/questions/9147357/brainfuck-interpreter):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdlib.h>
char m[9999], *n[99], *r = m, *p = m + 5000, **s = n, d, c;
main()
{
   for (read(0, r, 4000); c = *r; r++)
          c - ']' || (d > 1 ||
          (r = *p ? *s : (--s, r)), !d || d--), c - '[' || d++ ||
          (*++s = r), d || (*p += c == '+', *p -= c == '-', p += c == '>',
          p -= c == '<', c - '.' || write(2, p, 1), c - ',' || read(2, p, 1));
}

C語言果然很特別!!!

底下為其他語言實作出來的 brainfuck interpreters 與模擬實驗資料。
https://github.com/pablojorge/brainfuck

若您覺得文章寫得不錯,請點選文章上的廣告,來支持小編,謝謝。

If you like this post, please click the ads on the blog or buy me a coffee. Thank you very much.

這個網誌中的熱門文章