2014年1月21日 星期二

The devide and conquer for the Closest Pair Problem



演算法:

給一些平面上的點,找出這些相距最近的兩個點

1. 如果只有一點,回傳距離為無限大

2. 找一直線L垂直X軸並且分割S成一樣大小的兩半SL and SR
    持續分割,直到剩下兩點或一點就進行比較

3. merge時 找出SL 與 SR 兩點之中最小的距離,紀錄為d
    並且在L-d 與 L 裡找所有點a 看有沒有點在 L 與 L+d 裡並且Y值要介於 ay + d  與 ay - d
    如果有判斷它與a 的距離d'是否小於d, 如果小於,則d = d'

程式碼實作如下

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

struct vertex  //座標資料 
{
    float x;     //紀錄X位置 
    float y;     //紀錄Y位置 
};

struct min_pair //紀錄最短距離pair的資料 
{
    float x1;
    float y1;
    float x2;
    float y2;
    float min_len;   //紀錄最短距離  
};

float len(float x1,float y1,float x2,float y2) //計算兩點間的距離函式 
{
   float result;
   result = sqrt( fabs(x2-x1)*fabs(x2-x1) + fabs(y2-y1)*fabs(y2-y1) );    
   return result;
}

void per( struct vertex *vertex_arr1, int init, int end, struct min_pair *pair1) //per函式將所有點一分為二 init為初始注標 end為結尾注標 
{
    int i,j;
    float result,middle; 
    if( init == end - 1 )  //分割到剩兩點 直接計算距離 
    {   
       result = len(vertex_arr1[init].x, vertex_arr1[init].y, vertex_arr1[end].x, vertex_arr1[end].y); 
       if( result < pair1->min_len )
       {
          pair1->x1 = vertex_arr1[init].x;
          pair1->y1 = vertex_arr1[init].y;
          pair1->x2 = vertex_arr1[end].x;
          pair1->y2 = vertex_arr1[end].y; 
          pair1->min_len = result; 
       }      
    }
    else if(init == end)
        return;
    else
    {
       per(vertex_arr1, init, (init + end )/2, pair1);  //分割後 前半段帶入遞迴 
       per(vertex_arr1, (init + end )/2+1, end, pair1); //分割後 後半段帶入遞迴
       
       //開始merge 
       middle = ( vertex_arr1[(init+end)/2].x + vertex_arr1[(init+end)/2+1].x )/2; //middle紀錄要merge兩區域的中間X值 
       for(i=init;i<=(init+end)/2;i++)
       {
          if( vertex_arr1[i].x <= middle && vertex_arr1[i].x >= middle - pair1->min_len ) //左點的X值位在middle與middle-min_len之間 
          {
             for(j=(init + end)/2+1;j<=end; j++)
             {
                if( vertex_arr1[j].x >= middle && vertex_arr1[j].x <= middle + pair1->min_len ) //右點的X值位在middle+min_len之間 
                {
                   if( vertex_arr1[i].y + pair1->min_len >= vertex_arr1[j].y && vertex_arr1[i].y - pair1->min_len <= vertex_arr1[j].y ) //右點的Y值位在位在左點的Y值+min_len 與y值-min_len之間 
                   {
                      result = len( vertex_arr1[i].x, vertex_arr1[i].y, vertex_arr1[j].x, vertex_arr1[j].y);
                      if( result < pair1->min_len )
                      {
                          pair1->x1 = vertex_arr1[i].x;
                          pair1->y1 = vertex_arr1[i].y;
                          pair1->x2 = vertex_arr1[j].x;
                          pair1->y2 = vertex_arr1[j].y; 
                          pair1->min_len = result; 
                      } 
                   }         
                } 
             }                   
          }
       }
    }              
}

int main()
{
    int i,max=0,j,temp;
    char c[10];
    FILE *fp;
    fp = fopen("input.txt","r+"); 
    
    i=0;
    while(1)    //讀檔 計算有幾個點 
    {
       i++;
       if( fgets(c,10,fp) == NULL )
         break;
    }
    max = i-1; // 共有max個點
    
    fseek(fp,0L,SEEK_SET); //移回檔頭 
    
    struct vertex *vertex_arr = malloc( max * sizeof(struct vertex) );
    struct min_pair *pair = malloc( sizeof(struct min_pair) );
    
    //初始化
    pair->min_len = 1000;
    
    i=0;
    while(1)    //讀檔 並放入vertex 結構 
    {
       fscanf( fp,"%f,%f",&(vertex_arr[i].x) ,&(vertex_arr[i].y) );
       i++;
       if( (getc(fp)) == EOF )
         break;
    }
    
    //先按照X值由小到大排列 
    for(i=0;i<max;i++)
    {
       for(j=i+1;j<max;j++)
       {             
          if( vertex_arr[i].x > vertex_arr[j].x )
          {    
              temp = vertex_arr[i].x;
              vertex_arr[i].x = vertex_arr[j].x;
              vertex_arr[j].x = temp;
              
              temp = vertex_arr[i].y;
              vertex_arr[i].y = vertex_arr[j].y;
              vertex_arr[j].y = temp;
          }
          else if( vertex_arr[i].x == vertex_arr[j].x && vertex_arr[i].y > vertex_arr[j].y )
          {    
              temp = vertex_arr[i].y;
              vertex_arr[i].y = vertex_arr[j].y;
              vertex_arr[j].y = temp;
          }
       }             
    }
    
    per(vertex_arr,0,max-1,pair); //開始遞迴 
    
    printf("%.0f,%.0f\n",pair->x1,pair->y1);
    printf("%.0f,%.0f\n",pair->x2,pair->y2);
    printf("min_len = %.2f\n",pair->min_len);
    

    
    system("pause");
    return 0;
}

輸入請以讀入檔案方式。
例如:以下檔案內容,代表5個平面座標點
2,3
4,5
6,7
8,9
10,11

MIPS programing : show multiplication table of 9*9

Write a MIPS assembly program to show multiplication table 9*9
.text
.globl main
main:

addi $s0, $zero, 1      # i = 1

loop1:                  #for i loop
slti $t0, $s0, 10       #if(i<10)
beq  $t0, $zero, exit   #if(i>=10) go exit
addi $s1, $zero, 1      #else set j=1

loop2:                  #for j loop
slti $t0, $s1, 10       #if(j<10)
beq  $t0, $zero, label  #if(j>=10) go label

move $a0, $s0           #else printf i
li   $v0, 1      
syscall 

la   $a0, mul_str       #printf *
li   $v0, 4
syscall

move $a0, $s1           #else printf j
li   $v0, 1      
syscall

la   $a0, equ_str       #printf =
li   $v0, 4
syscall

mul  $a0, $s0, $s1      #else printf i*j
li   $v0, 1      
syscall

la   $a0, spa_str       #printf " "
li   $v0, 4
syscall

addi $s1, $s1, 1        #j++
j    loop2

label:
la   $a0, ch_row        #printf \n
li   $v0, 4
syscall

addi $s0, $s0, 1        #i++
j    loop1

exit:                   #exit
jr   $ra

.data
mul_str: .asciiz"*"
spa_str: .asciiz" "
ch_row:  .asciiz"\n"
equ_str: .asciiz"="

MIPS programing : separate a word

write a MIPS assembly probram to receive a string as the input in the console

window and output each separate word reversely line by line on the condole window

input: 

computer science is interesting

output: 

ertupmmoc

ecneics

si

gnitseretni


.text
.globl main
main:

li   $t0, 0                #$t0 = i , i=0
li   $t1, 0                #$t1 = j , j=0
lbu  $t3, spa_str($zero)   #$t3 = " "
lbu  $t4, ch_row($zero)    #$t4 = "\n"

li   $v0, 8                #讀字串
la   $a0, arr
syscall

loop1:

lbu  $t2, arr($t0)         #$t2 = value of arr[i]

beq  $t2, $t3, label1      #if(arr[i] == " ") goto label1
beq  $t2, $t4, label1      #else if(arr[i] == '\n') goto label1
addi $t0, $t0, 1           #else i++
j loop1

label1:
add  $s1, $zero, $t0       #$s1 = len , len = i
addi $s1, $s1, -1          #len--

addi $t5, $t1, -1          #$t5 = value of j - 1

loop2:                     #while(len != j-1)  

beq  $s1, $t5, label2      #if(len == j-1) goto labe2

lbu  $t2, arr($s1)         #$t2 = value of arr[len]
sb   $t2, c($zero)         #else c[0] = arr[len]

la   $a0, c                #printf c[0]     
li   $v0, 4 
syscall

addi $s1, $s1, -1          #len--
j loop2

label2:

la   $a0, ch_row           #printf \n
li   $v0, 4
syscall

lbu  $t2, arr($t0)         #$t2 = value of arr[i]
beq  $t2, $t4, exit        #if(arr[i] == '\n') 結束

addi $t1, $t0, 1           #j=i+1
addi $t0, $t0, 1           #i++

j    loop1

exit:
jr   $ra

.data
c:    .space 2
arr:  .space 100
spa_str: .asciiz" "
ch_row:  .asciiz"\n"

MIPS programing : evaluate factorial

wirte a MIPS assembly program to evaluate factorial.

factorial:
F(0)=1
F(1)=1
F(i)=i X (i-1) X (i-2) X .... X 1, if i>=2

1. user should input an interger 'i' in the console window.
2. output F(1), F(2),...,F(i) to the console window
3. hte program should include procedure call

ex. 
input: 5
outut: 1, 2, 6, 24, 120

.text
.globl main

fac:                        #遞迴函式名稱

addi  $t0, $zero, 1         #$t0 = 1  
beq   $a0, $t0, label       #if($a0 == 1)         
addi  $sp, $sp, -8          #else push堆疊
sw    $a0, 0($sp)
sw    $ra, 4($sp)
addi  $a0, $a0, -1          #$a0--
jal   fac                   #繼續遞迴  
lw    $a0, 0($sp)
lw    $ra, 4($sp)
addi  $sp, $sp,8
mul   $t0, $v0, $a0         #$t0放回傳值

la    $a0, spa_str          #printf ","
li    $v0, 4
syscall

add   $a0, $zero, $t0       #print mul 
li    $v0, 1       
syscall

add   $v0, $zero, $t0       #回傳 mul
jr    $ra

label:

addi  $a0, $zero, 1         #print 1 
li    $v0, 1       
syscall

addi  $v0, $zero, 1         #return 1
jr    $ra                   

main:

li     $v0, 5                #讀數字
syscall

move   $a0, $v0              #$a0存取數字
jal    fac                   #呼叫遞迴函式      
 
jr   $ra                     #結束程式

.data
spa_str: .asciiz","

DOS指令

將視窗改為30列、40行

mode con cols=40 lines=30

模擬樂園3-心得

前言:

整體來講,模擬樂園3是一款相當耐玩的遊戲,玩家需要有耐心,
而且還要注意到許多細節,才能讓樂園的收入源源不絕
不過我也是第一次玩有這麼多BUG的遊戲
被許多莫名的BUG浪費花了許多時間
花了兩周的時間把所有關卡都破完之後
頓時有種莫名的空虛感....

一些心得:

1 遊客剛進入樂園時,不會餓也不會渴,當然也不會需要上廁所跟進醫護站
也因此在每個關卡開始時,商店都不用先建立(服務台除外),
之後隨著時間流逝,注意遊客的狀態,如果開始有人肚子餓或是口渴的話,
才需要建立食物或飲料商店,換句話說,隨時注意遊客的狀態列表,
以建立相對應的遊樂設施。

2 在關卡剛開始時,道路並不會骯髒,遊樂設施也不會馬上故障,
隨著時間進行一旦發現道路出現垃圾或是設施故障了再僱用清潔員或維修員即可。

3 食物商店在建立時擺放位置要選好,絕對不要建立在道路交錯的區域,
否則遊客在亂丟垃圾時,亂丟的區域會非常廣,清潔員也難清理,
食物商店盡量建立在主要道路上或是道路的尾端,亂丟的區域才能限制住。

3 每個清潔員的負責區域要規劃好,越人煙稀少的區域負責的區域可以大一點,
越是遊客來往頻繁的區域負責的區域就要越小,且清潔員要訓練才能能跟上遊客亂丟垃圾的速度,附近也要盡量多擺放一些垃圾桶。

4 遊樂設施的價錢設定我都是依照遊樂設施的刺激度來設定價錢,
例如說刺激度2.5的設施我就設價錢為2.0~2.3之間,
也就是說遊樂設施中最賺錢的會是雲霄飛車(因為他的刺激度最高)!
但這只是我的方法,說不定有其他方法更好。

5 商店在販賣項目上通常有很多選項可以選擇,我建議賣最貴的就好(因為賺最多錢阿o.O)
設定的方式是設在遊客所能接受的最高的合理價錢,
如果遊客覺得這價錢物超所值的話,就提高價錢
直到遊客覺得太貴買不下手為止,
另外那些配料我建議都加下去即可(我覺得沒差)

6 雲霄飛車洗錢法:
等雲霄飛車的座位被遊客坐滿時,先暫停時間,將雲霄飛車按紅燈,
再切換為建築模式,再將建築模式關掉,再按綠燈,之後讓時間繼續,剛剛在坐位上的遊客就會出現在設施出口處,
而在路口處的遊客又會馬上購票進入,如此反覆進行洗錢即可。
洗錢法的前提: 雲霄飛車必須大排長龍,且出口要建立在地面較好,
否則一堆遊客突然出現在出口會導致一些遊客被推擠出去,可能會導致一些被擠出去的遊客迷路。

7 自己建立雲霄飛車的時機: 如果你的空間夠廣且錢夠多的話,選擇內建的雲霄飛車是省時又省力,
但如果地不夠或錢不夠的話,那還是只能選擇自己建立雲霄飛車,
自己建雲霄飛車之前,建議先存檔,因為自己建立雲霄飛車一定會遇到一個情況就是建好的部分須拆掉重蓋,
而拆掉時跟建立時的價錢不會等價,這中間的差價隨著設施的高度越高差價就越多,有可能會遇到還沒蓋完錢就不夠的窘境!
還有就是雲霄飛車在轉彎時如果速度太快會造成強度過大而刺激度變成0的情況發生。 解決的方法有三個 第一個是在轉彎前設置剎車裝置 第二個是在轉彎前提昇高度 第三個是設置彎道傾斜

8 模擬樂園3最大的BUG就是有時明明遊樂設施就沒問題,遊客卻卡在遊樂設施前進不去,遇到此問題只能選擇存檔再重新載入才能解決。

9 遊樂設施的規劃很重要,在越小的區域內蓋越多的遊樂設施是最好的,這樣之後維修員在維修時才不會大老遠走去維修

10 在關卡要VIP的任務時,一定要先規劃好VIP的行走路線,如果要搭乘雲霄飛車就將行走路線設定為雲霄飛車入口車站,如果是要觀賞煙火,則是先在一定點設置好拍照景點,再將行走路線設定在此拍照景點,當VIP走到此拍照景點時,再施放煙火即可,如果是要觀賞主題區域,則規劃的路線的附近就擺一堆符合此一主題景物即可。

11 有些關卡可以在遊客進樂園門口時收費,此時是海撈一筆的機會,一定要讓遊客的錢在進遊樂園時就給他花完,但要確保不會高到連進來的錢都不夠阿

12 在網路上爬聞看到有人說可以用 強迫付費法 ,也就是先用運輸設施載人到陌生的地方 然後遊客就會迷路,之後會去服務台買地圖,然後再坐運輸設施回去,不過本人親自試驗後發現有問題,遊客並沒有那麼聰明,會出現遊客找不到某某設施的入口的訊息,這個方法應該還需要改進

一些關卡的建議:

第5關: 驚魂之夜: 關鍵在於一開始是否就能憑自己蓋出符合破關條件的雲霄飛車,如果能蓋出則其它都不是問題,建議用 翻轉瘋狂鼠 來蓋

第7關: 布魯湖: 似乎會遇到VIP在樂園來來回回出不去的狀況,解決方法是在VIP出去之前把他抓起來,之後VIP就會莫名消失了,而任務也達成了o.O

第11關: 國家寶藏: 這一關遊客都不願意花大錢搭遊樂設施,導致要達成破關條件:遊樂設施總收入:900 根本不可能,最後還是打了密碼才破了這關

第14關: 宇宙景觀: 這一關有個莫名的BUG,就是一開始全部遊客會全部跑到服務台跟餅乾店之間來回買東西,解決方法就是不要建服務台,此時那些遊客才終於恢復理智肯去玩我蓋的雲霄飛車ˊˋ

第16關: 高山救援: 這一關有個速破法就是直接蓋三個內建的地層雲霄飛車-同溫層 即可破關

tesseract-ocr 3.X 訓練

tesseract-ocr 3.X 訓練

tesseract-ocr的訓練功能可以訓練出屬於自己的lang.traineddata 
例如說 我只想要辨識數字的話 那用tesseract-ocr 提供的eng.traineddata來進行辨識的話

正確率並不高 此時就可以用訓練功能來訓練出具有高正確度辨識數字的lang.traineddata

方法請參考下面這篇轉錄文章


最近在研究車牌辨識

找上了歷史相當久遠的Tesseract

Tesseract屬於開放原始碼,並在Google code中維護。

Tesseract的討論相當多,但是對於訓練(traning)的著墨是少之又少

幾乎千篇一律是Tesseract 2.0的翻譯文(直接從官網翻譯出來的文章)

所以本篇應該是全世界第一篇繁體中文Tesseract 3.0 training教學


詳細內容還是得參考官方網站:Traning Tesseract 3.0

以下是我整理的訓練步驟:



一. 先到Tesseract下載頁面下載兩個檔案:

tesseract-ocr-setup-3.00.exe
tesseract-3.00.1.exe.zip



二. 下載完後安裝 tesseract-ocr-setup-3.00.exe

然後再將tesseract-3.00.1.exe.zip解壓縮出來的執行檔tesseract-3.00.1.exe

覆蓋在剛剛的安裝目錄(Tesseract-OCR)裡。



三. 將所收集到的某字體的字母二值化後做成一張 .tif 的圖檔。
(小畫家即可製作)

如下圖所示:


eng.segoe.exp1.tif

將此圖檔(eng.segoe.exp1.tif)放在安裝目錄(Tesseract-OCR)下。



四. 系統管理員身分執行命令提示字元

到剛剛的安裝目錄底下(C:\Program Files (x86)\Tesseract-OCR),

執行下面的command line:
tesseract eng.timesitalic.exp0.tif eng.timesitalic.exp0 batch.nochop makebox

紅色部分是需要替換的文字,例子如下:
tesseract eng.segoe.exp1.tif eng.segoe.exp1 batch.nochop makebox

此時會產生一個.box的檔案(eng.segoe.exp1.box)。



五. 利用記事本打開.box檔(eng.segoe.exp1.box),

去修正裡面有錯的部分,也可利用一些軟體來修正。(詳情請參考Traning Tesseract 3.0)



六. 接著執行:
tesseract lang.timesitalic.exp0.tif lang.timesitalic.exp0 nobatch box.train

紅色一樣是需要替換的部分,例子如下:
tesseract eng.segoe.exp1.tif eng.segoe.exp1 nobatch box.train

此時會產生三個檔案:

.tr檔(eng.segoe.exp1.tr) .txt檔(eng.segoe.exp1.txt) .log檔(tesseract.log)

可以看一下log檔是否有錯誤發生。



七. 接著執行:
unicharset_extractor lang.timesitalic.exp0.box

紅色一樣是需要替換的部分,例子如下:
unicharset_extractor eng.segoe.exp1.box

此時會跑出一個檔案:unicharset。




八. 接著在安裝目錄(Tesseract-OCR)下新增一個純文字檔,

內容打上: 

本篇的例子如下:segoe 0 0 0 0 0

並另存新檔,存檔類型改成所有檔案,檔案名稱為:font_properties

這是用來做此字型的訓練內容(也就是.tif圖檔)的屬性設定,

意思就是:某字體 是否斜體 是否粗體 是否固定 是否是櫬線字體 是否是fraktur字體。
(屬性就是用0和1去控制。)



九. 接著執行:
mftraining -F font_properties -U unicharset lang.timesitalic.exp0.tr
(font_properties就是剛剛新增出來的純文字

紅色一樣是需要替換的部分,例子如下:
mftraining -F font_properties -U unicharset eng.segoe.exp1.tr

此時會跑出四個檔案:inttemp, mfunicharset, Microfeat, pffmtable。



十. 接著執行:
mftraining -F font_properties -U unicharset -O lang.unicharset lang.timesitalic.exp0.tr

紅色一樣是需要替換的部分,例子如下:
mftraining -F font_properties -U unicharset -O eng.unicharset eng.segoe.exp1.tr

此時會多出一個檔案:.unicharset(eng.unicharset)。



十一. 接著執行:
cntraining lang.timesitalic.exp0.tr

紅色一樣是需要替換的部分,例子如下:
cntraining eng.segoe.exp1.tr

此時會多出一個檔案:normproto。



十二. (最容易忽略的步驟)

接著把剛剛產生的五個檔案:
Microfeat, normproto, pffmtable, mfunicharset, inttemp

重新命名,在前面加上:lang. ,替換例子如下:
eng.Microfeat, eng.normproto, eng.pffmtable, eng.mfunicharset, eng.inttemp



十三. 接著執行:
combine_tessdata lang.

紅色一樣是需要替換的部分,例子如下:
combine_tessdata eng.

畫面會出現類似的文字:

Combining tessdata files
TessdataManager combined tesseract data files.
Offset for type 0 is -1
Offset for type 1 is 84
Offset for type 2 is -1
Offset for type 3 is 1453
Offset for type 4 is 698348
Offset for type 5 is 698912
Offset for type 6 is -1
Offset for type 7 is -1
Offset for type 8 is -1
Offset for type 9 is -1

依照上述的步驟執行的話,至少這四行紅色部分不能為-1,否則就是失敗!

此時會跑出最後一個檔案:.traineddata(eng.traineddata)

也就是最後的訓練文檔。



十四. 最後進行測試前,

需要先把產生出來的.traineddata(eng.traineddata)檔,

複製到執行目錄下的tessdata資料夾(記得先備份原本在裡面的eng.traineddata),

然後執行:
tesseract image.tif output -l lang

紅色一樣是需要替換的部分,我們拿之前的訓練圖檔來測試,例子如下:
tesseract eng.segoe.exp1.tif output -l eng

會產生一個output.txt

裡面就是Tesseract OCR 3.0透過你的訓練文檔來辨識出來的結果。


文章轉錄於http://yy-blogger.blogspot.com/2011/09/training-tesseract-ocr-30.html