UVa 134 - Loglan-A Logical Language

Problem

Loglan is a synthetic speakable language designed to test some of the fundamental problems of linguistics, such as the Sapir Whorf hypothesis. It is syntactically unambiguous, culturally neutral and metaphysically parsimonious. What follows is a gross over-simplification of an already very small grammar of some 200 rules.

Loglan sentences consist of a series of words and names, separated by spaces, and are terminated by a period (.). Loglan words all end with a vowel; names, which are derived extra-linguistically, end with a consonant. Loglan words are divided into two classes–little words which specify the structure of a sentence, and predicates which have the form CCVCV or CVCCV where C represents a consonant and V represents a vowel (see examples later).

The subset of Loglan that we are considering uses the following grammar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
A → a | e | i | o | u
MOD → ga | ge | gi | go | gu
BA → ba | be | bi | bo | bu
DA → da | de | di | do | du
LA → la | le | li | lo | lu
NAM → {all names}
PREDA → {all predicates}
<sentence> → <statement> | <predclaim>
<predclaim> → <predname> BA <preds> | DA <preds>
<preds> → <predstring> | <preds> A <predstring>
<predname> → LA <predstring> | NAM
<predstring> → PREDA | <predstring> PREDA
<statement> → <predname> <verbpred> <predname> | <predname> <verbpred>
<verbpred> → MOD <predstring>

Write a program that will read a succession of strings and determine whether or not they are correctly formed Loglan sentences.

Input and Output

Each Loglan sentence will start on a new line and will be terminated by a period (.). The sentence may occupy more than one line and words may be separated by more than one whitespace character. The input will be terminated by a line containing a single `#’. You can assume that all words will be correctly formed.

Output will consist of one line for each sentence containing either Good' orBad!’.

Sample input

1
2
3
4
la mutce bunbo mrenu bi ditca.
la fumna bi le mrenu.
djan ga vedma le negro ketpi.
#

Sample output

1
2
3
Good
Bad!
Good

Solution

其實這一題有比較好的解法,但是在正在學編譯器,因此把 LL(1) parser 運用在此題
,所以代碼會稍微長一點。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<sentence> -> <common_pre>
<sentence> -> DA <preds>
<common_pre> -> <predname> <suffix>
<suffix> -> <predclaim>
<suffix> -> <statement>
<predclaim> -> BA <preds>
<preds> -> <preds_head> <preds_tail>
<preds_head> -> <predstring>
<preds_tail> -> A <predstring> <preds_tail>
<preds_tail> -> l
<predname> -> LA <predstring>
<predname> -> NAM
<predstring> -> PREDA <predstr_tail>
<predstr_tail> -> PREDA <predstr_tail>
<predstr_tail> -> l
<statement> -> <verbpred> <statement_s>
<statement_s> -> <predname>
<statement_s> -> l
<verbpred> -> MOD <predstring>

為了要符合 LL(1) 的形式,要想辦法將 Grammar 符合規格,也就是每一條 production rule 的 predict set 不會有模稜兩個的情況,也就是說當我看到某個 non-terminal 在 stack 上,在去看下一個 token,然後可以對於 (non-terminal, token) 只找到一條規則去匹配。

predict set 是根據每一條 production rule 所產生的,也就是說這個推論可以產生的第一個 token 是什麼,假使有相同的前綴時,則必須拉出做調整。// 因為這將會造成模稜兩可的情況。

遇到

1
2
A -> BCx
A -> BCy

應調整成

1
2
3
A -> BCW
W -> x
W -> y

在 parsing 問題上,應避免替換的無限迴圈。
遇到

1
2
3
A -> AC
A -> X
A -> Y

應調整成

1
2
3
4
5
A -> WD
D -> CD
D -> lambda
W -> X
W -> Y

發現對照課本的 parsing function 撰寫起來才發現有些不完善的地方,對於 lldriver() 會沒有辦法應對輸入結束,要吐出 lambda 的問題。

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
#include <stdio.h>
#include <vector>
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <ctype.h>
#include <assert.h>
using namespace std;
class Production {
public:
string LHS;
vector<string> RHS;
int label;
Production(string L = "", vector<string> R = vector<string>()) {
LHS = L;
RHS = R;
}
void print() {
printf("%s -> ", LHS.c_str());
for(size_t i = 0; i < RHS.size(); i++)
printf("%s", RHS[i].c_str());
}
};
class Grammar {
public:
static const string lambda;
string start_symbol;
vector<Production> rules;
map<string, set<string> > first_set;
map<string, set<string> > follow_set;
map<string, bool> derives_lambda;
map<string, map<string, Production> > lltable;
map<string, bool> mark_lambda();
void fill_first_set();
void fill_follow_set();
bool isNonterminal(string token);
set<string> compute_first(vector<string> rhs);
set<string> get_predict_set(Production p);
void fill_lltable();
void lldriver(queue<string> tokens);
};
const string Grammar::lambda("l");
set<string> Grammar::compute_first(vector<string> rhs) {
set<string> result;
size_t i;
if(rhs.size() == 0 || rhs[0] == Grammar::lambda) {
result.insert(Grammar::lambda);
} else {
result = first_set[rhs[0]];
for(i = 1; i < rhs.size() &&
first_set[rhs[i-1]].find(Grammar::lambda) != first_set[rhs[i-1]].end(); i++) {
set<string> f = first_set[rhs[i]];
f.erase(Grammar::lambda);
result.insert(f.begin(), f.end());
}
if(i == rhs.size()
&& first_set[rhs[i-1]].find(Grammar::lambda) != first_set[rhs[i-1]].end()) {
result.insert(Grammar::lambda);
} else {
result.erase(Grammar::lambda);
}
}
return result;
}
/*
* please call get_predict_set() after fill_follow_set() and fill_first_set()
*/
set<string> Grammar::get_predict_set(Production p) {
set<string> result;
set<string> rfirst;
rfirst = compute_first(p.RHS);
if(rfirst.find(Grammar::lambda) != rfirst.end()) {
rfirst.erase(Grammar::lambda);
result.insert(rfirst.begin(), rfirst.end());
rfirst = follow_set[p.LHS];
result.insert(rfirst.begin(), rfirst.end());
} else {
result.insert(rfirst.begin(), rfirst.end());
}
return result;
}
/*
*
*/
void Grammar::fill_lltable() {
string A; // nonterminal
Production p;
for(size_t i = 0; i < rules.size(); i++) {
p = rules[i];
A = p.LHS;
set<string> predict_set = get_predict_set(p);
for(set<string>::iterator it = predict_set.begin();
it != predict_set.end(); it++) {
assert(lltable[A].find(*it) == lltable[A].end());
lltable[A][*it] = p;
}
}
}
bool Grammar::isNonterminal(string token) {
if(token[0] == '<' && token[token.length() - 1] == '>')
return true;
return false;
}
map<string, bool> Grammar::mark_lambda() {
bool rhs_derives_lambda;
bool changes;
Production p;
derives_lambda.clear();
/* initially, nothing is marked. */
for(size_t i = 0; i < rules.size(); i++) {
derives_lambda[rules[i].LHS] = false;
}
do {
changes = false;
for(size_t i = 0; i < rules.size(); i++) {
p = rules[i];
if(!derives_lambda[p.LHS]) {
if(p.RHS.size() == 0 || p.RHS[0] == Grammar::lambda) {
changes = derives_lambda[p.LHS] = true;
continue;
}
/* does each part of RHS derive lambda ? */
rhs_derives_lambda = derives_lambda[string(p.RHS[0])];
for(size_t j = 1; j < p.RHS.size(); j++) {
rhs_derives_lambda &= derives_lambda[p.RHS[j]];
}
if(rhs_derives_lambda) {
changes = true;
derives_lambda[p.LHS] = true;
}
}
}
} while(changes);
return derives_lambda;
}
void Grammar::fill_first_set() {
string A; // nonterminal
string a; // terminal
Production p;
bool changes;
mark_lambda();
first_set.clear();
for(size_t i = 0; i < rules.size(); i++) {
A = rules[i].LHS;
if(derives_lambda[A])
first_set[A].insert(Grammar::lambda);
}
for(size_t i = 0; i < rules.size(); i++) {
for(size_t j = 0; j < rules[i].RHS.size(); j++) {
a = rules[i].RHS[j];
if(!isNonterminal(a)) {
if(a != Grammar::lambda)
first_set[a].insert(a);
if(j == 0) { // A -> aXX
first_set[rules[i].LHS].insert(a);
}
}
}
}
do {
changes = false;
for(size_t i = 0; i < rules.size(); i++) {
p = rules[i];
set<string> rfirst = compute_first(p.RHS);
size_t oldsize = first_set[p.LHS].size();
first_set[p.LHS].insert(rfirst.begin(), rfirst.end());
size_t newsize = first_set[p.LHS].size();
if(oldsize != newsize)
changes = true;
}
} while(changes);
}
void Grammar::fill_follow_set() {
string A, B; // nonterminal
Production p;
bool changes;
for(size_t i = 0; i < rules.size(); i++) {
A = rules[i].LHS;
follow_set[A].clear();
}
follow_set[start_symbol].insert(Grammar::lambda);
do {
changes = false;
for(size_t i = 0; i < rules.size(); i++) {
/* A -> a B b
* I.e. for each production and each occurrence
* of a nonterminal in its right-hand side.
*/
p = rules[i];
A = p.LHS;
for(size_t j = 0; j < p.RHS.size(); j++) {
B = p.RHS[j];
if(isNonterminal(B)) {
vector<string> beta(p.RHS.begin() + j + 1, p.RHS.end());
set<string> rfirst = compute_first(beta);
size_t oldsize = follow_set[B].size();
if(rfirst.find(Grammar::lambda) == rfirst.end()) {
follow_set[B].insert(rfirst.begin(), rfirst.end());
} else {
rfirst.erase(Grammar::lambda);
follow_set[B].insert(rfirst.begin(), rfirst.end());
rfirst = follow_set[A];
follow_set[B].insert(rfirst.begin(), rfirst.end());
}
size_t newsize = follow_set[B].size();
if(oldsize != newsize)
changes = true;
}
}
}
} while(changes);
}
void Grammar::lldriver(queue<string> tokens) {
stack<string> stk;
string X;
string a;
Production p;
/* Push the Start Symbol onto an empty stack */
stk.push(start_symbol);
while(!stk.empty() && !tokens.empty()) {
X = stk.top();
a = tokens.front();
// cout << X << " " << a << endl;
if(isNonterminal(X) && lltable[X].find(a) != lltable[X].end()) {
p = lltable[X][a];
stk.pop();
for(int i = p.RHS.size() - 1; i >= 0; i--) {
if(p.RHS[i] == Grammar::lambda)
continue;
stk.push(p.RHS[i]);
}
} else if(X == a) {
stk.pop();
tokens.pop();
} else if(lltable[X].find(Grammar::lambda) != lltable[X].end()) {
stk.pop();
} else {
/* Process syntax error. */
puts("Bad!");
return;
}
}
while(!stk.empty()) {
X = stk.top();
if(lltable[X].find(Grammar::lambda) != lltable[X].end())
stk.pop();
else
break;
}
if(tokens.size() == 0 && stk.size() == 0)
puts("Good");
else
puts("Bad!");
return;
}
int aVowel(char c) {
switch(c) {
case 'a':case 'e':case 'i':case 'o':case 'u':
return 1;
}
return 0;
}
string token2symbol(const string &str) {
int n = str.length();
if (!islower(str[n - 1]) || !aVowel(str[n - 1])) {
return "NAM";
}
switch (n) {
case 1: return "A";
case 5:
n = aVowel(str[4]);
n |= ((aVowel(str[0]) << 4) | (aVowel(str[1]) << 3));
n |= ((aVowel(str[2]) << 2) | (aVowel(str[3]) << 1));
return (n == 5 || n == 9) ? "PREDA" : "UN";
case 2:
switch (str[0]) {
case 'g': return "MOD";
case 'b': return "BA";
case 'd': return "DA";
case 'l': return "LA";
}
}
return "UN";
}
void parsingProduction(string r, Grammar &g) {
static int production_label = 0;
stringstream sin(r);
string lhs, foo;
vector<string> tokens;
sin >> lhs >> foo;
while(sin >> foo)
tokens.push_back(foo);
Production p(lhs, tokens);
p.label = ++production_label;
g.rules.push_back(p);
}
int main() {
Grammar g;
parsingProduction("<sentence> -> <common_pre>", g);
parsingProduction("<sentence> -> DA <preds>", g);
parsingProduction("<common_pre> -> <predname> <suffix>", g);
parsingProduction("<suffix> -> <predclaim>", g);
parsingProduction("<suffix> -> <statement>", g);
parsingProduction("<predclaim> -> BA <preds>", g);
parsingProduction("<predclaim> -> DA <preds>", g);
parsingProduction("<preds> -> <preds_head> <preds_tail>", g);
parsingProduction("<preds_head> -> <predstring>", g);
parsingProduction("<preds_tail> -> A <predstring> <preds_tail>", g);
parsingProduction("<preds_tail> -> l", g);
parsingProduction("<predname> -> LA <predstring>", g);
parsingProduction("<predname> -> NAM", g);
parsingProduction("<predstring> -> PREDA <predstr_tail>", g);
parsingProduction("<predstr_tail> -> PREDA <predstr_tail>", g);
parsingProduction("<predstr_tail> -> l", g);
parsingProduction("<statement> -> <verbpred> <statement_s>", g);
parsingProduction("<statement_s> -> <predname>", g);
parsingProduction("<statement_s> -> l", g);
parsingProduction("<verbpred> -> MOD <predstring>", g);
g.start_symbol = "<sentence>";
g.fill_first_set();
g.fill_follow_set();
g.fill_lltable();
queue<string> SYMBOL;
for(string token; cin >> token && token != "#";) {
size_t pos = token.find('.');
if(pos == string::npos) {
SYMBOL.push(token2symbol(token));
//cout << token2symbol(token) << " ";
continue;
}
token.erase(token.length() - 1);
//cout << token2symbol(token) << endl;
if(token.length() > 0)
SYMBOL.push(token2symbol(token));
g.lldriver(SYMBOL);
while(!SYMBOL.empty())
SYMBOL.pop();
}
return 0;
}
Read More +

大神面試紀錄 Hardware Interview

本篇文章記錄身邊大神去面試時所遇到的神祕問題,對於現在大三的我只能旁觀,雖然在計算機組織這一類的硬體課程修完不久,但是看到這些問題,還真的不知所措。

面試的時候,可以帶上會協助您的小夥伴們一起面試,這一點相當地奇特,面試不再是單打獨鬥。當然,小夥伴有事也沒有關係,將可以使用線上 call out。

也是這些原因,才有機會參與這些問題的討論。

面試題目

以下附錄僅為討論,不代表正確答案。

面試採用漸進式,由高階至低階開始追問,同時也會問到 Hardware Algorithm

  • C/C++ 寫一個九九乘法表

    這邊就不多加以描述,通常採用兩層迴圈的寫法和一個 printf

    1
    2
    3
    for(int i = 1; i <= 9; i++)
    for(int j = 1; j <= 9; j++)
    printf("%d x %d = %d\n", i, j, i * j);
  • printf(format, arg) 怎麼實作的?

    請準備 stdio.h 開始查閱,不過當下要想出來還真的挺難的,不過在 linux 下,stdin, stderr, stdout 都是一個檔案形式,這是在呼叫 printf後會去導入的目標所在。但如何解決 arg,要查閱一下 va_start 怎麼運作的,若上網搜尋得到。

    printflink
    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
    #include <libioP.h>
    #include <stdarg.h>
    #include <stdio.h>
    #undef printf
    /* Write formatted output to stdout from the format string FORMAT. */
    /* VARARGS1 */
    int
    __printf (const char *format, ...)
    {
    va_list arg;
    int done;
    va_start (arg, format);
    done = vfprintf (stdout, format, arg);
    va_end (arg);
    return done;
    }
    #undef _IO_printf
    ldbl_strong_alias (__printf, printf);
    /* This is for libg++. */
    ldbl_strong_alias (__printf, _IO_printf);

    多變數到底是如何在這裡實作的,這一點要去考量 va_start,他會將參數位置丟入 va_list arg 中,但是如果參數個數或者是型態不符合 format,則運行的時候指針會指到錯誤的地方而噴出 RE,嘗試所及就到這裡了。

  • CPU 怎麼實作這些?

    CPU 的 pipeline 的五個階段和實作,IF, ID, EX, MEM, WB
    擷取指令、指定解析、執行運算、寫入記憶體、寫回暫存器。
    然後如何 pipeline,如果有類型的不同的時候,pipeline 會猜測到底要不要 branch,如果真的要 branch,要將 branch 之後的指令清掉 … 計組忘得一乾二淨。


  • 實作一個 n bits 加法器 adder,在平行的情況下,最多能多快。

    一般而言,要在 n 個 clock 後才能得到,這麼做就太慢了,之前上課有提到 carry lookahead adder, carry saved adder, … 等,這些雖然能在比一般時間更快得到是否要進位,但問題是要怎麼知道加完之後的結果!

    所以通常是切塊,然後賭前一塊到底會不會進位過來,把兩個答案都先算出,最後再利用 selector 去選擇正確的那個結果。

    如果單純是要得到是否會 overflow,最快可以在 O(log n) 時間內得到,採用分治的方式去考慮進位問題。

    分治算法看起來很接近線段樹的做法,藉由上一步,把兩種可能的答案都先算出,我們已經在 O(log n) 時間內把線段樹建出,接著對每一個元素做前綴的區間查詢,全部并行可以在 O(log n) 時間內完成,然後調用 selector 決定我們的答案。

    太多平行,平行處理的效能分析和處理資料的優先順序,很少在 ACM 使用,只好在此刻 yy 了。

  • 給很多區間 [l, r],接著詢問某個點 x 被多少區間覆蓋。

    線段樹處理,搭配區間的懶操作,直接對區間 [l, r] 所有元素 + 1 即可,在 O(n log n) 時間內建表,然後單點查詢 A[x] 的值為何,這種做法可以動態支持。

  • 給一般圖進行匹配,假設不使用標準的帶花樹算法,使用自己的算法,請問誤差為多少。

    貪婪策略:
    每次對一個鄰居最少的點,找到其鄰居 (他的鄰居也最少) 進行匹配,然後把這些點都拿掉,繼續遞迴匹配。

    至於誤差-只能想辦法找到一個反例,來確定誤差一定大於某個值,要詳細證明什麼的,大概可以順便報一篇論文了。

    天祥反例

    一群人在思考反例,想了很久都沒有結果,最後由天祥大神找到一個誤差有 20% 的反例,詳細如上圖所述,如果一開始拿到 AD 或者是 HI,則會把匹配數最多拿到 4 個,事實上為 5 個。

結論

相當可怕,學得都忘得一乾二淨,無法精準回答。

Read More +

ICPC 4020 - Hard Rode

Problem

The road to Shu is hard, even harder than climbing to the blue sky. A poem by Li Po from Tang Dynasty 1,200 years ago described the difficulty in travelling into Sichuan.

\epsfbox{p4020.eps}

However the above old saying is no longer true as a convenient communication network of railway, highway, waterway and air transport has come into being. Railways cover a total mileage of 2,693 km, consisting of five trunk lines including the BaojiChengdu and Chengdu-Chongqing railways, eight feeder lines and four local railways. The total mileage of highways stretches to nearly 80,000 km, ranking at the forefront in China. Now a highway network with the provincial capital of Chengdu as the center radiating to all cities in the province has been formed. A total of 500 km of expressways have been built. It is very easy to transfer passengers and freights between Sichuan and other provinces. After a nationwide railway speed acceleration launched in last year, trains can be allowed to run at a speed above 120 km per hour. However, the average speed of a train depends on its make-up. There is only single railway track between stations from Baoji to Chengdu, A primary task for dispatchers is to arrange for trains to meet and pass each other. You are requested to write a program to make the arrangement for a series of trains of different speeds from one station to its next station in the least amount of time.

What you should pay attention to while writing this program is that since there is a single railway track from Baoji to Chengdu, two trains are not allowed to pass the same spot at the same time, or they would be collision, and that because of the limited staff, there should be a fixed interval time between two trains out of the station, what’s more, the trains could pull into the station at the same time, but never get out at the same time.

Input

The input consists of several test cases. Each test case begins with a line containing 3 integers L (1 <= L <= 100000) , N (1 <= N <= 8) and T (1 < T < 10000) which indicate the distance between two stations (unit is meter), the number of trains, as well as the interval time of adjacent trains when trains leave the start (unit is second).

The next N lines contain average speeds of N trains (unit is m/s).

A single line with L = 0 indicates the end of input file and should not be processed by your program.

Output

For each test case, your output should be in one line with the minimum time(round to integer) needed by the trains pulling into the terminal as indicated in the sample output.

Sample Input

1
2
3
4
5
6
7
8
100000 6 300
3
4
5
6
2
1
0

Sample Output

1
Case 1: 101500

Solution

題目描述:

從 陝西省寶雞市 到 四川省成都市 有一條鐵軌,而這一條鐵軌一次有好幾台火車在運行,但是不會發生追撞事件,然而每台火車的速度不一樣,而且站務人員會每隔固定時間發一班車,在不發生碰撞的情況下,最後一台火車到達成都市的最少時間為何?

題目解法:

在這樣的情況,有可能慢車先出發,然在在 T 秒之後,快車恰好追到慢車,這樣的情況會更好。但是實際數據應該長成什麼樣子沒有仔細考慮。

如果使用慢車最後出發,這樣能確保一定不會產生碰撞,但不確定是否是最佳解。

所以使用 O(n!) 的窮舉來計算答案。

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
#include <stdio.h>
#include <algorithm>
#include <math.h>
using namespace std;
int main() {
int L, N, T, cases = 0;
long long speed[10];
while(scanf("%d %d %d", &L, &N, &T) == 3 && L) {
int p[10] = {};
for(int i = 0; i < N; i++) {
scanf("%lld", &speed[i]);
p[i] = i;
}
sort(speed, speed + N);
long long ret = 0x3f3f3f3f;
do {
double start = T, lastTime = L / speed[p[0]];
for(int i = 1; i < N; i++) {
double time = L / speed[p[i]] + start;
if(time >= lastTime) {
start += T;
lastTime = time;
} else {
lastTime = 0x3f3f3f3f;
break;
// start += T + (lastTime - time);
}
}
ret = min(ret, (long long) ceil(lastTime));
} while (next_permutation(p, p + N));
printf("Case %d: %lld\n", ++cases, ret);
}
return 0;
}
Read More +

UVa 10320 - Cow Trouble! Help Please

Problem

There are many classic problem with cows/goats and grass field in the world of mathematics. One of such problems is shown in the picture (left one) below. A goat is tied at point C which is on the edge of a round field with a rope of length R. One has to determine the length R when the cow can eat 50% grass of the round field. But in this problem we will consider a slightly different scenario which is shown in the second figure. In a field of infinite area there is a house ABDC. A cow is tied with pillar B of the house with a rope of length R. The length and width of the house is l and w. If the house was not there the cow could eat all the grass in the round green field (The round green field is a circle of radius R). But the presence of the house will certainly reduce his ability as the cow cannot enter the house nor can any part of the rope. You will have to determine the area the cow can now roam around and eat grass.

Fig 1: A classic Problem with a Goat

Fig 2: The Current Classic Problem with Cow

Input

The input file contains several lines of input. Each line contains three floating point numbers l, w and R as described in the problem statement. All numbers are less than 10000. Input is terminated by end of file.

Output

For each line of input produce one line of output. This output denotes the area that the cow can cover by not entering the house. This value should contain ten digits after the decimal point. Output will be checked with special judge. So you should not worry about very small precision errors.

Sample Input

1
2
10 5 5
10 4 8

Sample Output

1
2
58.9048622548
163.3628179867

Solution

題目描述:
有一頭牛被一根長度 R 的繩子拴在一棟建築物 ABCD 的 B 點上,草地的範圍為無限大,求牛可以吃到的草的範圍。Input 會給l, w 和 R 表示建築物長寬和繩長;l, w, R < 10000。Output 要輸到小數點第 10 位。

題目解法:

就是根據 R 的四種情況去討論,其中最麻煩的是對於 R > l + w,這種情況會需要討論重疊的區域,算出三角形夾角然後分別算兩個扇形面積,再弄回重疊區域三角形中的哪一塊有草。

其實這一題的題解網路上是有的,但是 PO 出來是因為自己卡在 WA,原因是這樣子的,假使 A = 0.5 * BA = B / 2,這兩者寫法並無不同,但是後者的精準度比較好,很多次的 WA 都掛在這一點。

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
#include <stdio.h>
#include <math.h>
#include <algorithm>
using namespace std;
int main() {
double w, l, r;
const double pi = acos(-1);
while(scanf("%lf %lf %lf", &l, &w, &r) == 3) {
if(w > l)
swap(w, l);
double area;
if(r <= w)
area = r * r * pi * 3 / 4.0;
else if(r <= l)
area = r * r * pi * 3 / 4.0 + (r - w) * (r - w) * pi / 4.0;
else if(r <= l + w)
area = r * r * pi * 3 / 4.0 + (r - w) * (r - w) * pi / 4.0
+ (r - l) * (r - l) * pi / 4.0;
else {
double a = atan2(l, w), b, c;
double la, lb, lc;
la = hypot(w, l);
lb = r - w;
lc = r - l;
b = acos((la*la + lb*lb - lc*lc)/(2*la*lb)) + a;
c = acos((la*la + lc*lc - lb*lb)/(2*la*lc)) + (pi /2) - a;
area = r * r * pi * 3 / 4.0;
area += (r - w) * (r - w) * (pi - b) / 2;
area += (r - l) * (r - l) * (pi - c) / 2;
area += lb * la * sin(b - a) / 2;
area -= w * l / 2;
}
printf("%0.10lf\n", area);
}
return 0;
}
Read More +

計算型智慧 程式作業

Computational Intelligence 計算型智慧

本課程將會有三階段程式,將三個算法依序加入程式中,為了要實作這些算法,必須先作出模擬器。

Lv. 0 模擬器

在一個 2D 平面中,以一個圓形當作車子,道路為線段的方式進行描述。為了在隨後的擴充,將地圖內容物分成可行區域多邊形和障礙物多邊形。

  • 要支持車子下一秒的擺角,並且按下推動鍵會根據方程式進行移動。
  • 可以將車子兩側的感測器進行角度調整,並且可以根據感測器角度進行距離計算。
  • 並且在呈現 2D 平面時,附加顯示座標軸的比例。

完成圖

由於私心,有一些額外產物。

  • 完成可以自動置中的設定
  • 提供地圖縮放和讀取操作
  • 製作地圖產生器 (other project)
  • 模擬器的彈木遊戲番外篇 (other project)
  • 提供運行欄位的縮起

Lv. 1 模糊系統

車子運行公式

運行公式

其中 Φ(t) 是模型車與水平軸的角度,b 是模型車的長度,x 與 y 是模型車的座標位置, Θ(t) 是模型車方向盤所打的角度,我們對模擬的輸入輸出變數限制如下:

-40 deg <= Θ(t) <= 40 deg

寫一個程式使用模糊系統、理論模擬車子行徑,並且想辦法最佳化。

  • 考慮擴充性,以後車子的運行公式可能會替換,因此建造一個 Engine class
  • 模糊系統的幾個特性拆分成數個 method,並且使用不同的數學計算採用繼承關係。
  • 最困難得地方在於計算函數交集,採用離散取點是最後的手段

函數實驗

  1. 經過實驗,三個感測器分別坐落於前方 (90度)、左右各偏轉 50 度左右的夾角比左右 90 度更好。用以應付多變的轉角彎度,防止模稜兩可的擺動。
  2. 不管哪裡種去模糊化系統,由於題目限制擺角於 [-40, 40] 度之間,因此很容易在加權平均、質心、離散化而導致邊界擺角的機率甚低,當越多函數則稀釋程度越高。因此函數定義可以超過限制擺角,只需要在去模糊化時,約束至條件內即可。
  3. 設計函數時,要討論向其中一個方向偏轉,也就是不能設計過於對稱的圖形,否則將會在死胡同裡打轉。

函數設計

d1 為前方感測器,d2 為右方感測器,d3 為左方感測器。

  1. d1 歸屬函數為
    d1 歸屬函數
    d2, d3 歸屬函數為
    d2, d3 歸屬函數
  2. 函數式加權平均
    If d3 large, θ = 55
    If d2 large, θ = -55
    If d2 medium, θ = -40
    If d3 medium, θ = 40
    If d1 large and d2 small, θ = -30
    If d1 large and d3 small, θ = 30
    If d1 small, θ = -60
    
    規則如上述七條,以最簡單的常數關係。測試結果還算不錯。
    if condition, then statement,statement 中的變數不存在於 condition 去做調整,後來發現由於太複雜去討論而一直失敗。
  3. 重心法、最大平均法、修正型最大平均法、中心平均法
    為了方便計算連續函數,採用離散取點,間隔 0.1 抓取 [-60, 60] 之間的點座標。對於以下四種方法討論於簡單彎道的過程記錄:
    If d3 large
    If d3 large
    If d2 large
    If d2 large
    If d2 medium
    If d2 medium
    If d3 medium
    If d3 medium
    If d1 large and d2 small
    If d1 large and d2 small
    If d1 large and d3 small
    If d1 large and d3 small
    If d1 small, θ = -60
    If d1 small

    • 重心法
      當中表現最為穩健,而且路徑平滑。
    • 最大平均法
      由於定義的函數較為簡單,而且大多都是以梯形的方式存在,雖然能在機率高的情況下走完全程,路徑中會呈現左右擺盪情況。並且大多都已極值的方式出現。
    • 修正型最大平均法
      情況會稍微好一些,但是對於並沒有向重心法一樣的圓滑曲線,仍是因為原先的函數設計所惹的問題。

    對於某一種方法而去定義相關的函數進行測較好,而在最大平均法的部分,必須使用較具有變化性的曲線,否則很容易在極值狀況下擺動。


Lv 2. GA

RBF

「放射狀基底函數網路 RBF」,基本上,其網路架構如圖1所示,為兩層的網路;假設輸入維度是p ,以及隱藏層類神經元的數目是J ,那麼網路的輸入可以表示成:

其中選用高斯型基底函數:

其中 x = (x1,x2,…,xp) 、mj = (mj1,mj2,…,mjp) 、|| || 代表向量絕對值
適應函數為:

請用實數型基因演算法,找出wj , mj , σj,在不同的數字J下,最好的基因向量 (例如J為9、輸入x為3維向量,則表示基因向量是1+9+3x9+9=46維度的向量,請注意這裡不是指族群數;又例如J為7、輸入x為3維向量,則表示基因向量是1+7+3x7+7=36維度的向量)下,評估函數E(式1)為越小越好。其中基因向量維度公式為 1+J + pJ+J = (p+2)J+1維向量( ,w1 , w2 ,.., wJ , m11, m12,.., m1p, m21, m22,.., m2p,.., mJ1, mJ2,…, mJp, σ1, σ2, …, σJ)。

參數說明 :

  • N 作業1產生的N筆成功到達目的訓練資料(換不同起始點)
  • yn 表示訓練資料的方向盤期望輸出值 P.s.如果配合wj值的範圍為0~1之間,在此則必須把yn由-40~+40度正規化到 0~1之間;如果不想正規化 就必須把 wj的值範圍調整到 -40~40之間 範圍為0~1之間
  • Wj 範圍為 0~1 (或是-40~40)之間 P.s.此需配合訓練集的yn跟F(n)值範圍,所以皆需正規化到0~1之間;若不正規化,wj的值範圍為 -40~40之間
  • mj 範圍跟X範圍一樣,如以提供的範例檔則為 0~30
  • σj 範圍為0~10之間;也可以設定更大的範圍做探討。

實作概要
把參數編碼成 DNA,使用 GA 算法改變 DNA,然後用之前的模糊系統的結果,讓 RBF 接近之前定義的模糊系統。需要拿模糊系統的一些測資當作訓練資料,收集之後餵給 GA 做訓練。

實驗方法

  1. 核心代碼,對於每次迭代,著手適應、選擇、交配、突變。
  2. 細部 “交配” 代碼,採用實數型的分離和聚集兩種情況。
  3. 細部 “突變” 代碼,採用微量雜訊,唯一不同的地方在於,這個微量雜訊會根據當前數值的比例有關,為了避免大數字對於微量雜訊的干擾不夠強烈。

實驗結果

  1. 運行結果 J = 3, p = 3
    ( ,w1 , w2 ,.., wJ , m11, m12,.., m1p, m21, m22,.., m2p,.., mJ1, mJ2,…, mJp, σ1, σ2, …, σJ) = (0.163568 0.851471 1.594151 1.379580 13.092824 0.000505 12.611789 30.000000 0.000368 18.715240 0.000850 2.708967 30.000000 6.307677 7.584529 10.000000)
  2. 訓練數據為 “map2” 走一圈的訓練資訊,約為 500 筆。因為 “map0” (即本次作業給的地圖) 的複雜度不夠以至於無法充分表達原本設計的模糊系統。也就是根據當初模糊系統設計,收集的數據的多樣性和連續性不足。

實驗分析

根據 RBF 的神經元,這裡可以越少越好,在程式中,神經元個數(J) 設置 3 個。運行時採用輪盤式選擇,讓適應能力好的,具有較高的機會繁殖,採用一個隨機變數去挑選。而在基因方面使用實數型基因的形式。

  1. 運行時,突變機率和突變比例相當重要,由於相同適應能力好的物種量很多,只保留其中一部分即可,因此可以將突變機率0.5 左右,太低則會造成進化速度太慢,太高則容易失去原本適應好的物種,導致整體適應度的震盪。
  2. 另外設置突變的比例,也就是該段實數值上下調整的比例。
    g.getDNA()[i] = g.getDNA()[i] + ratio * Math.random() * g.getDNA()[i];
    藉由上述的式子,將 ratio 設置成一個調變比例,來製造爆炸效應的規模。而在運行時,突變效果還能接受。
  3. 原本運行時,只將 DNA 片段的值任意放置,並且約束在不會超出函數的定義域,在收斂速度上有明顯加速。但為了符合作業需求,將每個參數約束在指定範圍內,在收斂速度慢了一截,在預期結果並沒有很好。
  4. 在不同地圖收集的預期資訊,會針對不同車子感測器的角度有所差異,因此不能拿不同型的感測數據,訓練出來的 RBF 不能給另外一台車子來使用,除非使用的感測器角度相當接近。
  5. 對於死路的轉彎,在神經元個數(J) 等於 2 的時候,運行結果較為不佳,但是在本次作業中,並不會有這種數據的出現,也不用考慮這種情況。但是當神經元個數少時,GA 算法的運行速度是相當快的。

Lv. 3 PSO

請利用Particle swarm optimization替換掉 GA 部分。

  • AgentNum(粒子數)
  • IterationNum(疊代數),
  • MaxV(最大速度),
  • MaxX(最大值範圍),
  • Weights(兩個權重),
  • Convergent_Error(停止條件)
  • 並畫出 fitness function 曲線變化 (每一次疊代的最佳fitness值),最後訓練完成的F(x)當作規則 請跑出車子軌跡。

實驗分析

一開始參數為

1
2
3
4
5
6
public int sandSize = 512;
public double maxVelocity = 0.5;
public double maxXposition = 0.5;
public double weightOfCognition = 0.5;
public double weightOfSocial = 0.5;
public double nearRatio = 0.5;

PSO 1

PSO 2

  • 基本上 maxXposition 沒有用到,調整其他比例可以發現端倪。

  • 從圖 PSO 2 中可以發現到,PSO 與 GA 最大的不同在於,如果 GA 調用部分突變的情況下,很高的機率會發現適應曲線是嚴格遞減的,但是在 PSO 由於會飛來飛去,有可能會來回震盪。

  • 當所有粒子逼近最佳解時,仍會以原本的速度繼續飛行,而原本屬於最佳位置的點也會開始模仿其他粒子飛行,如此一來就會造成震盪。

PSO 3

  • 其震盪的高低決定於鄰近模仿的權重,如果鄰近的粒子效果不好,模仿的權重高的時候,會導致整個最佳解有嚴重的偏離。由 PSO 3 中可以驗證這個震盪問題。

  • 如果對於全局模仿權重調高,很容易造成處於群聚粒子不容易分離,也就是很難在早到其他的區域最佳解,會在很快的時間內進入某一個區域最佳解,從這一點中可以發現 PSO 真的具有快速收斂的性質。

  • 設定 maxVelocity 是將整個向量長度 (歐幾里得距離) 壓縮,這將可以控制查找的精密度,如果設定太小會導致收斂速度明顯下降。將 maxVelocity = 10 時,適應函數看起來會有比較高的進展,由於會將每個參數約束,只要速度不是太小影響就不大。

  • 當速度上限增加時,粒子碰到牆的機會也會上升,由於搜索範圍的可能性涵蓋邊界的情況增加,如果最佳解在邊界,在這樣的情況就會更好。

  • 在代碼中,鄰近的定義為最近粒子中排名前 20% 的最佳解。如果採用對於常數長度,這種情況上不容易觀察,也就是說不能直接找到鄰近模仿的規模該定義在何處。

  • 運行結果 J = 3, p = 3
    ( ,w1 , w2 ,.., wJ , m11, m12,.., m1p, m21, m22,.., m2p,.., mJ1, mJ2,…, mJp, σ1, σ2, …, σJ) = (0.117271 1.270048 0.000000 0.805823 15.845525 0.000000 21.613452 13.509075 0.000000 0.000000 30.000000 0.000000 10.505934 10.000000 10.000000 5.238101)

Github Download

Download

Blog

Read More +

UVa 12717 - Fiasco

Problem

Natasha always mixes up things in her algorithm classes. Last week Prof. Chhaya Murthy gave the class a classical problem -finding shortest path to all nodes from a special node of a weighted graph. Before that in another class, the professor taught them Prim’s and Dijkstra’s algorithms which are similar looking but does very different things. Always confused, poor Natasha submitted something
very similar to the Prim’s algorithm (actually we should call it Natasha’s lgorithm), she didn’t test it properly. Pseudocode of her algorithm looks as follows

uva 12717

After submission, she showed the code to a friend who is good at algorithms. That friend immediately found the aw. Natasha was really terrifed and started to cry. Then there came the guys, the guys who liked her. They jumped at the opportunity and got hold of the dataset to be used to test the solutions. Your friend Rehan is one of those guys. He really wanted to impress Natasha. He asked you to change the dataset in such a way that Natasha’s algorithm gives the right result. After the change, Rehan will somehow be able to put the modified data with the old timestamp. Rehan told you that:

  1. The dataset has T test cases.
  2. Each case contains a connected weighted undirected graph with n nodes and
    m edges.
  3. Each case will contain a source node source.
  4. Edge weights are positive and distinct.

To avoid suspicion, Rehan asked you not to change which vertices the edges connect, or the overall set of edge weights. You may only reorder which weights are assigned to which edges

Input

The first line of the input denotes the number of datasets T (1 < T <= 15). T sets of case will follow. Each case will start with a triplet of numbers n (2 <= n <= 2500), m (1 <= m <= 25000) and source (1 <= source <= n) | the number of nodes, the number of edges and the starting node respectively.

Each of the next m lines will contain a triplet of numbers (u, v, w) meaning that there is an edge between node u and node v with weight w (1 <= w <= m). Nodes are numbered from 1 to n.

It is guaranteed that there is no duplicate or self-edges in the input and the graph is connected. In each given graph, all edge weights will be distinct.

Output

For each set of input, print one set of output. First line of a set should be of the format, Case X: where X is the case number. Then print each edge triplet | one triplet (u, v, w) in each line separated by a single space. The edges should be printed in the order given in the input. If there is more than one solution to a dataset, any one of them will do.

Sample Input

1
2
3
4
5
6
7
8
9
10
11
1
7 9 5
2 4 2
1 4 8
7 2 6
3 4 7
5 7 5
7 3 9
6 1 1
6 3 4
5 6 3

Sample Output

1
2
3
4
5
6
7
8
9
10
Case 1:
2 4 7
1 4 9
7 2 3
3 4 8
5 7 1
7 3 4
6 1 5
6 3 6
5 6 2

Solution

題目描述:
// 給一個錯誤代碼,修改測資,使錯誤代碼輸出正確。
在一門演算法課程中,上到了最短路徑算法,教授指出了 Prim 和 Dijkstra 算法之間的不同,課堂上有個妹子很不小心搞混了這兩個算法,甚至弄出了更奇怪的程序,這代碼其中有缺陷,她和朋友分享代碼後,沒想到朋友瞬間戳出輸出錯誤的測資,妹子一時之間哭了出來。

現在為了擄獲芳心,請修改測資,使得錯誤代碼有一樣的正確輸出。

題目解法:
這個妹子錯的地方在於,更新的時候,沒有將為探訪點的情況加以考慮,直接以 BFS 的方式丟入,那麼在隨後更新的路徑長就不會被丟入 Priority Queue,這樣在 Dijkstra 的更新順序上就會出錯。也就是說,只要 Graph 的權重依據 BFS 順序 符合由小到大,不會出現隨後更新的問題即可。

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
77
78
79
80
81
82
83
84
#include <stdio.h>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
using namespace std;
vector<int> g[2505];
int used[25005];
int in_x[25005], in_y[25005], in_w[25005];
map< pair<int, int>, int> shortest_tree;
void bfs(int source) {
queue<int> Q;
int u, v, e = 0;
Q.push(source), used[source] = 1;
while(!Q.empty()) {
u = Q.front(), Q.pop();
for(int i = 0; i < g[u].size(); i++) {
v = g[u][i];
if(used[v]) continue;
used[v] = 1;
shortest_tree[make_pair(u, v)] = in_w[e], e++;
Q.push(v);
}
}
}
int main() {
int testcase, cases = 0;
int n, m, source;
int x, y, w;
scanf("%d", &testcase);
while(testcase--) {
scanf("%d %d %d", &n, &m, &source);
for(int i = 1; i <= n; i++) {
g[i].clear();
used[i] = 0;
}
shortest_tree.clear();
for(int i = 0; i < m; i++) {
scanf("%d %d %d", &x, &y, &w);
g[x].push_back(y);
g[y].push_back(x);
in_x[i] = x, in_y[i] = y, in_w[i] = w;
}
for(int i = 1; i <= n; i++)
sort(g[i].begin(), g[i].end());
sort(in_w, in_w + m);
bfs(source);
printf("Case %d:\n", ++cases);
map< pair<int, int>, int>::iterator it, jt, kt;
int e = m - 1;
kt = shortest_tree.end();
for(int i = 0; i < m; i++) {
it = shortest_tree.find(make_pair(in_x[i], in_y[i]));
jt = shortest_tree.find(make_pair(in_y[i], in_x[i]));
if(it != kt)
w = it->second;
else if(jt != kt)
w = jt->second;
else
w = in_w[e], e--;
printf("%d %d %d\n", in_x[i], in_y[i], w);
}
}
return 0;
}
/*
1
7 9 5
2 4 2
1 4 8
7 2 6
3 4 7
5 7 5
7 3 9
6 1 1
6 3 4
5 6 3
*/
Read More +

UVa 12716 - GCD XOR

Problem

Given an integer N, Find how many pairs (A, B) are there such that gcd(A, B) = A xor B where 1 < B < A <= N. Here gcd(A, B) means the greatest common divisor of the numbers A and B. And A xor B is the value of the bitwise xor operation on the binary representation of A and B.

Input

The First line of the input contains an integer T (T < 10000) denoting the number of test cases. The following T lines contain an integer N (1 <= N <= 30000000).

Output

For each test case, print the case number first in the format, Case X: (here, X is the serial of the input) followed by a space and then the answer for that case.
There is no new-line between cases.

Explanation
Sample 1:
For N = 7, there are four valid pairs: (3, 2), (5, 4), (6, 4) and (7, 6).

Sample Input

1
2
3
2
7
20000000

Sample Output

1
2
Case 1: 4
Case 2: 34866117

Solution

首先,嘗試把 N <= 1000 的情況列下來,列出 gcd(A, B) = C = A xor B

其中滿足 B < A and A mod C = 0,然後我們又觀察到 A&C == C,也就是說看 bitmask 的話,C 是 A 的 subset。

經由這一點,我們可以先寫出一個不會過 (TLE) 的做法,對 A 找到所有因子 C,查看是否符合 A&C == C,但是這樣的做法很容易知道時間效率不夠好。

下面是一個最簡單的版本。

TLE version
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
#include <stdio.h>
#include <algorithm>
#include <vector>
using namespace std;
#define maxL (30000000>>5)+1
#define GET(x) (mark[(x)>>5]>>((x)&31)&1)
#define SET(x) (mark[(x)>>5] |= 1<<((x)&31))
int mark[maxL];
int P[5500], Pt = 0;
int F[30000005] = {};
void sieve() {
register int i, j, k, l;
SET(1);
int n = 50000;
for(i = 2; i <= n; i++) {
if(!GET(i)) {
for(j = i + i; j <= n; j += i)
SET(j);
P[Pt++] = i;
}
}
}
vector< pair<int, int> > factor(int n) {
int on = n;
vector< pair<int, int> > R;
for(int i = 0, j; i < Pt && P[i] * P[i] <= n; i++) {
if(n%P[i] == 0) {
for(j = 0; n%P[i] == 0; n /= P[i], j++);
R.push_back(make_pair(P[i], j));
}
}
if(n != 1) R.push_back(make_pair(n, 1));
return R;
}
void make(int idx, int n, int m, vector< pair<int, int> > &v) {
if(idx == v.size()) {
if((n&m) == m)
F[n]++;
return;
}
int a = m, b = v[idx].first;
for(int i = v[idx].second; i >= 0; i--)
make(idx + 1, n, a, v), a *= b;
}
int main() {
sieve();
vector< pair<int, int> > ff;
for(int i = 1; i <= 30000000; i++) {
ff = factor(i);
make(0, i, 1, ff);
}
int testcase, cases = 0, n;
scanf("%d", &testcase);
while(testcase--) {
scanf("%d", &n);
int ret = 0;
for(int i = 1; i <= n; i++)
ret += F[i];
printf("%d\n", ret - n);
}
return 0;
}

接著,使用其他的考慮方式,既然 (n, n + 1) 是已知解,其倍數解 (kn, k(n + 1))也是考量的一部分。

總歸一句,觀察 orz。

accepted
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
using namespace std;
int F[30000005] = {};
int main() {
for(int i = 1; i <= 30000000; i++) {
for(int j = i << 1; i + j <= 30000000; j += i) {
if(((i + j)&(j)) == j)
F[i + j]++;
}
}
for(int i = 1; i <= 30000000; i++)
F[i] += F[i-1];
int testcase, cases = 0, n;
scanf("%d", &testcase);
while(testcase--) {
scanf("%d", &n);
printf("Case %d: %d\n", ++cases, F[n]);
}
return 0;
}
Read More +

作業系統 Thread 程式作業

C++ Thread 練習

作業要求

作業一:應用 Thread 撰寫任意程式(語言不限),繳交方式 FTP:140.115.155.192(選擇自己學號的資料夾上傳) 帳:os2014 密碼不知道者請再問助教,檔案名稱: “作業一學號姓名_第幾版” ex:”作業一10252200x林大乖_v1” 繳交內容:壓縮檔(報告word 以及 程式碼,執行檔),截止日期:2014/05/25(日)23:59

程式功能

兩個 n*n 矩陣相乘,在最基礎的 O(n^3) 效能上,使用 Thread 等相關技巧使其加速。

運行

  • Mac OSX

    $ clang -pthread x.cpp -o x
    
  • Linux

    $ gcc -lpthread x.cpp -o x
    

測試效率

  • Mac OSX and Linux
    $ time ./a.out
    

測試結果

測試環境為 2013 Mac Air 1.3 GHz Intel Core i5 上對 1024 x 1024 的矩陣進行相乘的統計結果。

  • matrix.cpp
    最原始的一般寫法,直接使用 O(n^3) 。運行時間約在 20 秒左右完成。
  • matrix[v2].cpp
    使用轉置矩陣後,在進行運算,這種方式是加快作業系統中的快取層,速度提升至約在 4 秒左右完成。
  • matrix[v3].cpp
    建立在 matrix[v2].cpp 的基礎上,使用 4 條 thread,分別對處理的 column 進行分割操作,速度約在 2 秒內完成。
  • matrix[v4].cpp
    matrix[v3].cpp 類似,調整 thread 的數量,查看效率變化,但是速度沒有更快的趨勢。

結論

Thread 多不代表會比較快,而是該程式佔有 CPU 的機會比較高,使用的資源就會比較多。在整個作業系統中,Thread 多上下文切換的次數就會上升,對於作業系統整體效率是下降的,但是又不能沒有 Thread,看起來像是所有程式同時在運行,也要防止落入死機無法動彈的情況。

而在 thread 上面會發現,當數量達到一定程度後,速度就不會上升,有一部分是因為 CPU 超頻運作。

超頻(英語:Overclocking)是把一個電子配件的時脈速度提升至高於廠方所定的速度運作,從而提升性能的方法,但此舉有可能導致該配件穩定性下降。

可能是長時間閒置的 CPU 發現有大規模的工作運行,把時脈速度升起。

代碼

matrix.cpp

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
#define MAX_N 1024
double A[MAX_N][MAX_N];
double B[MAX_N][MAX_N];
double C[MAX_N][MAX_N];
void multiply(double A[][MAX_N], double B[][MAX_N], double C[][MAX_N]) {
for (int i = 0; i < MAX_N; i++) {
for (int j = 0; j < MAX_N; j++) {
double sum = 0;
for (int k = 0; k < MAX_N; k++) {
sum += A[i][k] * B[k][j];
}
C[i][j] = sum;
}
}
}
int main(void) {
//clock_t start, finish;
//start = clock(); // start time clock
for (int i = 0; i < MAX_N; i++) {
for (int j = 0; j < MAX_N; j++) {
A[i][j] = i;
B[i][j] = j;
}
}
multiply(A, B, C);
//finish = clock(); // finish time clock
//printf("Time: %lf\n", (double)(finish - start)/1000);
return 0;
}

matrix[v2].cpp

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
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <time.h>
using namespace std;
#define MAX_N 1024
double A[MAX_N][MAX_N];
double B[MAX_N][MAX_N];
double C[MAX_N][MAX_N];
void trans(double A[][MAX_N]) {
for (int i = 0; i < MAX_N; i++) {
for (int j = i+1; j < MAX_N; j++) {
swap(A[i][j], A[j][i]);
}
}
}
void multiply(double A[][MAX_N], double B[][MAX_N], double C[][MAX_N]) {
trans(B);
for (int i = 0; i < MAX_N; i++) {
for (int j = 0; j < MAX_N; j++) {
double sum = 0;
for (int k = 0; k < MAX_N; k++) {
sum += A[i][k] * B[j][k];
}
C[i][j] = sum;
}
}
}
int main(void) {
//clock_t start, finish;
//start = clock(); // start time clock
for (int i = 0; i < MAX_N; i++) {
for (int j = 0; j < MAX_N; j++) {
A[i][j] = i;
B[i][j] = j;
}
}
multiply(A, B, C);
//finish = clock(); // finish time clock
//printf("Time: %lf\n", (double)(finish - start)/1000);
return 0;
}

matrix[v3].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
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#define MAX_N 1024
double A[MAX_N][MAX_N];
double B[MAX_N][MAX_N];
double C[MAX_N][MAX_N];
void trans(double A[][MAX_N]) {
double x;
int i, j;
for (i = 0; i < MAX_N; i++) {
for (j = i+1; j < MAX_N; j++) {
x = A[i][j];
A[i][j] = A[j][i];
A[j][i] = x;
}
}
}
#define MAX_THREAD 4
void *multiply(void *arg) {
int id = *((int *)arg);
int st = id * MAX_N / MAX_THREAD;
int ed = (id + 1) * MAX_N / MAX_THREAD - 1;
int i, j, k;
for (i = st; i <= ed; i++) {
for (j = 0; j < MAX_N; j++) {
double sum = 0;
for (k = 0; k < MAX_N; k++) {
sum += A[i][k] * B[j][k];
}
C[i][j] = sum;
}
}
return NULL;
}
int main(void) {
puts("Thread version");
//clock_t start, finish;
//start = clock(); // start time clock
int i, j;
for (i = 0; i < MAX_N; i++) {
for (j = 0; j < MAX_N; j++) {
A[i][j] = i;
B[i][j] = j;
}
}
trans(B);
pthread_t *threads;
threads = (pthread_t *) malloc(MAX_THREAD * sizeof(pthread_t));
for (int i = 0; i < MAX_THREAD; i++) {
int *p = (int *) malloc(sizeof(int));
*p = i;
pthread_create(&threads[i], NULL, multiply, (void *)(p));
}
for (i = 0; i < MAX_THREAD; i++) {
pthread_join(threads[i], NULL);
}
//finish = clock(); // finish time clock
//printf("Time: %lf\n", (double)(finish - start)/1000);
return 0;
}

其他

雖然作業可以有其他語言的相關 Thread,在 Java 作品方面可以查看 Github。

Read More +

論文選讀 Agile Development and User Experience Design ...

課程論文心得

首先,所讀這一篇的論文名稱為『Agile Development and User Experience Design Integration as an Ongoing Achievement in Practice』 所以著重點在于用戶體驗設計要怎麼融入敏捷開發的探討。

什麼是用戶體驗?先說說 UX Designer 做些什麼嗎

第一次轉UX Designer就上手(上)

第一次轉UX Designer就上手(下)

看完內容後,總之 UX Designer 算是一種設計師,與理工背景的開發人員可是天差地遠,

他們比起宅宅開發者來說,要與用戶產生關係,繪畫、表達、心理學、與現實溝通,開發人員擁有技術並且完成目標,但是行為卻不是一般使用者,最簡單的說法是『為什麼做出來的軟體只有開發者會用?』如果在軟體的需求面向是開發者們的話,這也許還不是問題,但是要做出給一般人使用的軟體,這樣是行不同的。但是開發者的邏輯古怪到自己都不明白的機率是挺高的,因此會有 UX Designer。

借由 UX design,軟體將會有耐用性、易上手、輕便化 … 等,這讓我突然想到總是會開發出根本用不著的功能,或者是相當難操作的功能。

但是要把 UX design 完全融入是有困難的,因為邏輯思維不同的人,是很難協調的,又或者會互相侵蝕。

開始談談論文的內容

這一篇論文,主要是 觀察 ,也沒有觀察到所有目前的運行方式。換句話說,不會存在唯一一個好的開發方式,根據組織的規模和文化背景,所適用的運行方式也會有所不同,在理論上百分之百照抄別人的做法運用在自己的發開方式,但是可以藉由論文中觀察的幾點來學習我們的開發方式大概需要的是哪些面向需求。

而當開發人員是怎麼樣類型的人,就可能需要運用哪些方式,這些都是需要去探討的。外國也許按照它們的方式走成功,但並不代表按照一樣的作法一樣會成功,這就所謂 新方法論 。新方法論沒有絕對的方法,說來有點玄,也就是沒有固定方法就是方法!

由於組織規模不同,彼此部門又要互相協調,在設計與開發之間的不同在於時間的週期不同,工作類型的方式不同,如果是大規模組織,『提高內聚力,降低耦合度』因此專業分工的類型將會在 Study 1 中被提及,這麼考量是有它的原因在的。

而文藝復興時期的全才-達文西類型,則可以在 Study 2 中被提及,每一個人都必須學習一點別人的領域的項目,這樣可以互相考量、共同承擔,來分散思考風險。但這樣對於一般人是相當困難的,如果是菁英組織的運行,大概就是長得這樣。

在最後的 Study 3 中,雖然使用傳統的瀑布式開發,這種神祕的設計大於開發組合相當異常,但也許是一種新的導向。在往後的時代中,當技術相對成熟穩定時,面對的將會是設計需求大於開發需求。


RESEARCH METHOD

這裡提供三種 Ethnography (社會學的一種寫作文本,中文翻譯:民族誌) 來探討敏捷開發人員和用戶體驗設計師的配置方式。

A. In the field

(田野調查的區域,民族誌運用田野工作來提供人類社會學的描述研究)

總共有三個組織中,四個團隊參與本次研究,參與研究的團隊中正在進行 UX 設計,而我們主要的工作是觀察他們的工作運行情況。

共計在三種情況進行觀察。

  • Study 1: 觀察時間為兩個星期
  • Study 2: 觀察時間為六天
  • Study 3: 觀察時間為兩天

其中 Study 1, 2 過程中會涵蓋 Sprint 會議,而 Study 3 則只有涵蓋開發人員和設計師共處一室。

B. Thematic analysis

(專題分析)

C. Study participants

(研究參與者)

ACCOUNTS OF THE DAY-TO-DAY INTEGARTION WORK

Study 1

這項研究在 14 位 developer 和 2 位 UX designer,這兩位 UX designer 並不屬於 developer team,而是屬於該公司的 UX design team,因此他們並沒有參與 Sprint meeting、Standup、Retrospective,當然開發人員也沒有參與 design meeting,developer team 和 design team 在不同樓層工作。

開發人員描述道『將 UX design 與 agile develop 分開,是基於管理層面。』這很大的原因在於要將 UX 創造力發揮到極限,不能讓軟體工程的問題阻擋他們思考。

而在 Sprint meeting 中觀察到,UX designer 會提供一個 UX design 在 sprint meeting 前送達。但在隨後的 UX design 版本則是偶爾送到 developer 手上,直到 sprint 的最後一天仍持續修改,這表示著 developer 無法依靠 UX design 考慮相關的實作項目,所以大多數的關於 UX 工作將會分配在最後幾個小時來完成。

然而如果詢問 UX designer 是否已經意識到自己時間分配所造成的影響,則他們會回答說『沒有』。但事實上,在發送 UX design 時就已經造成相當多的問題。

[UX designer]: “That was never explicitly said to me … I thinks there was and still is visibility issues to do with what they’re doing and what their timeline are.”
這表示 UX designer 從來不知道這些事情,也從來沒有直接地被告知這項問題,也是因為不知道 developer 的運行工作和時程表。

當要澄清或者是修改 UX design 時,develop 會將問題寫下,並且要求 UX designer 在他們的 feedback meeting 中討論。

[senior UX designer]: “The visual designs are pretty much parallel to the wireframes … what kind of feedback do you want to give ?”
資深 UX designer 表示道:所有的視覺設計都是在線框 (wireframe) 的基礎上完成的,能有什麼樣的反饋?

developer 並不完全了解 UX design,只能依靠 UX designer 的批准和重構,這 “批准” 主要是對於敏捷開發中的維護和支持的組成用途,但是當 developer 向 designer 發問時,往往是溝通失敗的。

2pm: There is some problem with the visual design from UX [hadn’t arrived yet]. Project manager’s been trying to get in touch with the interaction designer, however can not get in contact - mobile switched off. Project manager has heard that UX are moving desks (not all the visual designs have been delivered since the developer feedback session).

上述內容看得不明白,難道是就是單純 PM 得不到完整的 visual design,因此也就不能向互動工程師聯繫嗎?

Study 2

小型手機軟件開發的公司,這一間公司有兩支團隊,每一隊有 5 名 developer 和 1 名 UX designer。其中 UX designer 設計非功能性描述和 screen mock-ups,這幾個項目將可以幫助 developer 參考進行開發。

mock-ups 相當於一種 prototype 可以支持動作的描述,通常會帶有基礎的 action code,關於 mock-ups 的相關工具有很多,但是通常價格不菲,邊設計還能自動幫忙產生代碼是不容易的工作。網路上找不到相關的解釋,就先這樣頂替了,相較於一般手繪製圖和文件描述,這樣的方式更為容易了解。

在他們會議中,每一個角色(QA, scrum master, product owner, UX designer, developer) 都參與討論會議,創造共同意識和共享決策責任。

9:30 [developer]: How should tabs appear, is ad always right-most tab ? … UX designer tells the developer what whould happen: As new chats come in ad tab moves over to right-most tab.

開發人員: 分頁到底該如何呈現,而廣告要固定在最右側的分頁嗎? … UX designer 告訴我們應該「當新的聊天訊息進來時,廣告要移動到最右邊的分頁嗎?」

[Product owner]: Still waiting on response for some questions from the client.

Product owner: 這仍然在等待顧客的回報訊息

[QA]: Kind of hard to do QA if we don’t have the answer … wayry of starting stories without behaviours which means we might have to change thins and that wastes time.

QA: 這件事情很難做確認,如果我們沒有接收到回報,做的任何修改也許只是浪費時間,無法確定品質。

[UX designer]: Do them anyway, they might need to go through a number of iterations before they agree this is what they want … it’s better of they see something working as soon as posssible.

UX designer: 先把這問題放一旁,這需要多次迭代才能確定這是他們想要的 … 如果他們需要高效工作,則這個方式可能比較好。

[QA]: What if an ad coes in at exactly the same time as a chat ?

QA: 那如果廣告作為一個新的聊天訊息出場的話呢?

[UX designer]: That’s extreme edge case and probably very hard to test. So leave it. QA agrees.

UX designer: 這是一個相當極端的方式,非常難測試,所以先不要管這個。

這些對話通常在每一天的工作中進行,詢問另外一個團隊問題,而另外的團隊注意這些問題。

[UX designer]: “… if developers come up with issues, these are discussed on a case-by-case basis as they come up … There are lots of small things which are easily worked out on a day-to-day basis”

UX designer: 如果 developer 在每一個 case 基礎上想出了問題,這些小問題很容易在每日解決。

從會議過程中,主要,我們可以發現的每一個角色都參與了 UX design,同時也在職權外貢獻了他們的想法 - 例如 UX designer 建議在極端情況忽略 QA testing,而在 QA 沒有指責 UX designer 所做的 QA 決策,他們接受這項意見並沒有發出任何評論。其次,在這個 UX design 討論議題中也一起討論了 non-UX 的問題(如這個 QA testing 問題)。

這次的會議的下一步行動,包括實現解決方案和傳達問題給客戶。所有的角色參與討論導致決策同時考慮的設計價值和技術考量。

而在會議中,有 developer 向其他人簡單說明,他在網路空間上有一些之前專案製作時替換視覺設計的文件可以參考,這可以讓 UI designer 可以接受這些更改的項目,增加信服力。

[UX designer]: Sometimes the developers’ decisions are better, as they are much closer to the implementation. 有時候開發者的決策會更好,因為他們更接近實作。

Study 3

在非常小的公司進行,有 1 名 developer 和 2 名 UX designer,他們擅長瀑布形式的開發,在開發過程中 developer 和 UX designer 在同一間辦公室工作,目標是使得設計和開發更接近。正如 Study 1 的時候,發現 developer 會根據 UX designer 設計的線框 (wireframe) 和 visual design 努力地工作。

觀察兩天後的結果如下:

在一般的情況下,UX designer 通常和 developer 在不同樓層工作,developer 也表示他們很少去找 designer 討論遭遇的問題。

[developer]: We often find things that don’t work, but we don’t go back to design. We just do our best. 我們常發現很多事情並不會 work,但是並沒有回去檢討設計,單純盡力而為。

在這種共同工作的情況下,designer 給予了一些回應。

[UX designer]: This style of working means we can collaborate in order to get our points across, rather than dictate because there’s no communication going on. 這種工作方式可以讓我們的觀點交換,而不是因為沒有通通合作的命令。

[Head of UX]: And when we first met with the client, the first thing they did was to present to us the findings of that initial research. So we can then go and propose what would bee the next step, which is quite different from any other type of work we’ve been invited to get involved in.

在大多數的情況,不確定性,設計師在現場談話的記錄:

[visual designer]: I just feel like I’m not getting any-where.

[IA]: I also feel that way.

但是對於另一個情況下

[developer]: Just cover the site map.

developer 和 designer 共同工作

  • 可以相互討論跟顧客對談時的內容 (當時在場的感受不同)
  • 目標計劃做些什麼,訊息可以提前知道。(想要做第二個版本)
  • 「這個設計會不會很難實作?」這些問題將會很快被反應出來。

[developer]: It would probably be just as easy.


THEME FOR ACHIEVING INTEGRATION

A. Integration as expectations about acceptable behaviour

在 Study 1 中可以發現,如果將兩者隔離,很容易造成規劃的浪費和開發者在最後期限前超時工作。UX designer 同時跨越多個專案進行設計,丟出設計之後就沒有可接收開發者的反饋,只有等到 sprint meeting 的反應才重新設計,這時可能已經為期已晚。

在 Study 2 中可以發現,頻繁確保兩者之間有相互支持,並且在 UX design 和代碼之間可以互相溝通,相較于 Study 1 上,溝通性有上升,但這些遭遇的問題將會在每個人參與討論會議的過程中的一部份。

在 Study 3 中,每一個成員都有機會被其他角色問到自己領域的問題,同時也可以在與客戶討論中的觀察,在隨後討論。這都歸因于他們在同一間辦公室工作。

B. Integration as mutual awareness

相對于 Study 1 中,Study 2, 3 擁有了共同享有決策的責任,同時提高了互動上的認識。

Read More +