Long Luo's Life Notes

每一天都是奇迹

By Bertrand Russell

Francis Bacon, a man who rose to eminence by betraying his friends, asserted, no doubt as one of the ripe lessons of experience, that ‘knowledge is power’. But this is not true of all knowledge. Sir Thomas Browne wished to know what song the sirens sang, but if he had ascertained this it would not have enabled him to rise from being a magistrate to being High Sheriff of his county. The sort of knowledge that Bacon had in mind was that which we call scientific. In emphasising the importance of science, he was belatedly carrying on the tradition of the Arabs and the early Middle Ages, according to which knowledge consisted mainly as astrology, alchemy, and pharmacology, all of which were branches of science. A learned man was one who, having mastered these studies, had acquired magical powers. In the early eleventh century, Pope Silvester II, for no reason except that he read books, was universally believed to be a magician in league with the devil. Prospero, who in Shakespeare’s time was a mere phantasy, represented what had been for centuries the generally received conception of a learned man, so far at least as his powers of sorcery were concerned. Bacon believed—rightly, as we now know—that science could provide a more powerful magician’s wand than any that had been dreamed of by the necromancers of former ages.

英国一位靠出卖朋友而声名大噪的人——弗兰西斯·培根曾说:“知识就是力量。”这无疑是一句成熟的经验总结。托马斯·布朗爵士曾想弄清希腊神话中的海妖究竟唱什么歌,然而即使他确实清楚了,也不能帮他从一个地方长官提升为国家的高级行政长官。培根心目中的知识是指我们所说的科学知识。在强调科学的重要性时,他陈腐地承继阿拉伯和中世纪早期的传统,把知识看作主要由占星学、炼金术和药物学组成,这些都是科学的分支。一位精通这些学科的学者就是获得魔术般力量的人。11 世纪初,教皇西尔维斯特二世除了读些书外,并没有别的理由,就被普遍地看作是一个与魔鬼结盟的魔术师。普罗斯帕罗,在莎士比亚的时代只是一个虚构的人物,但几个世纪以来却代表着人们普遍接受的学者的概念,至今就其法力而言是人们所关注的。培根相信——正确地说,就如现在我们所知道的——科学能够提供比从前巫师任何幻梦还更有力的魔术师的魔杖。

The renaissance, which was at its height in England at the time of Bacon, involved a revolt against the utilitarian conception of knowledge. The Greeks had acquired a familiarity with Homer, as we do with music hall songs, because they enjoyed him, and without feeling that they were engaged in the pursuit of learning. But the men of the sixteenth century could not begin to understand him without first absorbing a very considerable amount of linguistic erudition. They admired the Greeks, and did not wish to be shut out from their pleasures; they therefore copied them, both in reading the classics and in other less avowable ways. Learning, in the renaissance, was part of the joie de vivre, just as much as drinking or love-making. And this was true not only of literature, but also of sterner studies. Everyone knows the story of Hobbes’s first contact with Euclid: opening the book, by chance, at the theorem of Pythagoras, he exclaimed, ‘By God, this is impossible’, and proceeded to read the proofs backwards until, reaching the axioms, he became convinced. No one can doubt that this was for him a voluptuous moment, unsullied by the thought of the utility of geometry in measuring fields.

培根在世时,英国的文艺复兴达到高峰,它包含一种对功利主义的知识概念的反抗。希腊人之熟悉荷马,有如我们熟悉音乐厅的歌曲,由于他们欣赏荷马,而不觉得是在忙于追求学问。但16世纪的人若不首先具备相当的语言学知识,就不能着手研究荷马。他们敬佩希腊人,并且又不愿意置身在他们的欢乐之外;因此在读那些古典著作时,他们总在仿效希腊人。在文艺复兴时,学习是生活乐趣的一部分,如同饮酒或性爱一佯。不仅对文学是这样,对那些较严肃的学科来说也是如此。人们都知道霍布斯首次接触欧几里德几何学的故事:一次他偶然翻开书,读到毕达哥拉斯定理,他大声叫道:“上帝,这是不可能的。”于是回头继续读它的证明,直至读到公理时,他才信服了。没有人会怀疑,对霍布斯来说,这一时刻如同耽迷酒色,然而想到几何学在测量土地中的效用,这种情绪被纯化了。

It is true that the renaissance found a practical use for the ancient languages in connection with theology. One of the earliest results of the new feeling for classical Latin was the discrediting of the forged decretals and the donation of Constantine. The inaccuracies which were discovered in the Vulgate and the Septuagint made Greek and Hebrew a necessary part of the controversial equipment of Protestant divines. The republican maxims of Greece and Rome were invoked to justify the resistance of Puritans to the Stuarts and of Jesuits to monarchs who had thrown off allegiance to the Pope. But all this was an effect, rather than a cause, of the revival of classical learning, which had been in full swing in Italy for nearly a century before Luther. The main motive of the renaissance was mental delight, the restoration of a certain richness and freedom in art and speculation which had been lost while ignorance and superstition kept the mind’s eye in blinkers.

确实,文艺复兴发现了古典语言与神学相关的实际用途。新感受到的古典拉丁文的最初成果之一,就是不再相信编造的教皇教条和康士坦丁的捐赠。在拉丁文圣经和希腊译文圣经之间出现的偏差,使得希腊文和希伯来文成为新教神学家准备争论的一个必要组成部分。希腊与罗马的共和主义被用来证明清教徒与斯图亚特王朝、耶稣会会员与那些不再忠顺于教皇的君主之间的对抗是合理的。但所有这一切都是路德以前在意大利将近一个世纪自由发展的古典学术之复兴的结果,而不是其原由。文艺复兴的主要动机是精神上的欢愉,是复兴在艺术和思维中曾经出现过、但后来由于无知和迷信蒙住了我们的心灵而失落的那种丰富而自由的精神。

阅读全文 »

By Long Luo

今天重新刷了下手机,结果之前的备份都是2个月前的,刷完机我就想了下,我手机必备App是哪些呢?

列出了下面这张必备App清单:

新闻

  1. ZAKER: 查看新闻,根据你的爱好订阅文章
  2. 新浪微博:作为微博的重度用户,微博的媒体属性很强,大于其社交属性。我的不少朋友已经告别微博了,但是我还是喜欢微博。与其在微信上与熟悉的人聊天,不如在微博上和陌生人交流。
  3. 网易新闻:交互做的很好,163评论常有亮点。

SNS·社交

  1. QQ: 不用说。超级App,超好的用户体验,本土化,非常强大。
  2. 微信:不用说。腾讯系的很多产品都极大的提高了我们的工作效率和生活效率,一定要用好他们。
  3. 微博:我用微博主要是学习和了解别人的很多观点,还能够结交很多朋友。
  4. LINE:主要是微博和微信的熟人太多,有些东西不好在朋友圈里吐槽,但LINE没有一个熟人,所以可以很方便的在朋友圈里吐槽发泄:-)
  5. LinkedIn:工作商务类的,也有很多干货。

阅读·电子书

  1. 知乎日报:打发碎片时间,同时了解很多各行各业的知识。
  2. 知乎:重度知乎用户,可以看到更大的世界,拓展你的思维界限。
  3. 多看阅读:电子书,在阅读体验我个人觉得是最好的,书的资源很多而且也不是很贵。

理财

  1. 支付宝:不用说。
  2. 招行手机App:主要用的都是招行卡。
  3. 招行信用卡:平常能刷信用卡的地方绝对使用信用卡。

消费·购物

  1. 手机淘宝:Shopping
  2. 京东:买书,购物等
  3. 美团:看电影,吃饭,订餐等O2O

股票证券类

  1. 广发证券:目前炒股的账户在广发。
  2. 自选股:腾讯系出品的一款炒股软件,美中不足的是交易不支持广发证券。
  3. 东方财富:炒股和市场行情。

交通出行

  1. 百度地图:国内最好的地图产品,对于我重度地图爱好者是真爱。
  2. Google地图:在国内被百度地图秒杀,但国外的话就是Google地图了。
  3. 爱帮公交:公交地铁线路图,还可以离线查询。
  4. 12306:火车出行必备

电子邮件

  1. QQ邮箱:很好用的一个电子邮件客户端,同时对Gmail的支持性很好。
  2. 邮箱大师:网易出品的一个邮箱客户端,这2个都可以。

时间管理·便签

  1. FitMemo: 非常轻量级的一款便签类,美中不足的是好久没有更新了,不支持云同步功能。
  2. Any.Do:支持多平台同步,非常好的时间管理App,用了就知道。
阅读全文 »

By Long Luo

上次在迅雷面试的时候,遇到了一个算法题,题目是:

有一个很长很长的字符串,全部都是由大写字母组成,要求求出其中每个字母在这个字符串中出现的次数。 不允许使用STL中的方法。

当时拿到这个题目,我首先想到了以下几个方法:

  1. 穷举法,一个个比较,最后算出每个字母出现的次数,这种方法可行,但不轻巧与优雅。
  2. 每个字符与’A’想减,会得到一个,统计下这个值出现次数,和方法1类似。(事后回顾这个已经很接近了,但是还是没能完美解决。)

其实这个题目,事后回顾起来,是比较简单的,但遗憾的是,当时未能在规定时间内解答出来,导致未能通过面试,拿到Offer,在此将这个题目记录下来。

重新认真解答这个题目。

How?

其实这个题目有一个很简单的解决方法,新建一个数组,大小为26,将字符串中每个字符都与'A'相减,得到的就是每个字母在数组中的元素下标值,我们最后得到的这个数组就是每个字母在这个字符串中出现的次数。

根据以上分析,可以写出如下代码:

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
package com.algorithm.alphabetSort;

/**
* 有一个很长的字符串,其中都是一些字母,求其中每个字母出现的次数(大小写区分)。
*
* @author luolong
* @date: 2015-04-12 00:54:17
*
*/
public class AlphabetSort {
private static String str = "AWQEYIOAHDHDKKLDLAHFHJALAFHANNAFGJCXCKBZCQIEOPADHAZBZVCFGCSHDJCKCLDMDHFAKAIIAYQO";

private static int[] outArray = new int[26];

public static void main(String[] args) {
AlphabetSortString(str);
printSortArray(outArray);
}

public static void AlphabetSortString(String str) {
char cTemp;

for (int i = 0; i < str.length(); i++) {
cTemp = str.charAt(i);

outArray[cTemp - 'A']++;
}
}

private static void printSortArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
char c = (char) ('A' + i);
System.out.print("" + c + "=" + arr[i] + " ");
}
}

}

引申与扩展一

如果字符串不仅仅是大写字母,而是大小写字母都存在的情况下,那应该如何写呢?

其实也很简单,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
if (cTemp >= 'A' && cTemp <= 'Z') {
/**
* 大写字母
*/
outArray[cTemp - 'A']++;
} else if (cTemp >= 'a' && cTemp <= 'z') {
/**
* 小写字母
*/
outArray[cTemp - 'a' + 26]++;
} else {

}
阅读全文 »

By Long Luo

最近在学习Java多线程时,遇到了一个下面的笔试题,题目如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
编写一个程序,程序会启动4个线程,向4个文件A,B,C,D里写入数据,每个线程只能写一个值。 
线程A:只写A
线程B:只写B
线程C:只写C
线程D:只写D

4个文件A,B,C,D。

程序运行起来,4个文件的写入结果如下:
A:ABCDABCD...
B:BCDABCDA...
C:CDABCDAB...
D:DABCDABC...

网上搜索了下,好像还是一个Google笔试题,这个问题涉及到的知识点有:多线程, 并发, , 线程间通信

个人分析过程:

  1. 创建4个线程;
  2. 每个线程在输出时都需要加锁;
  3. 操作文件的代码要加锁;
  4. 一个线程完成之后,通知下一个要执行的线程;

根据以上分析,可以写出如下代码:

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
package com.imlongluo.Practise;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Practise {

public static void main(String[] args) {

final Task task = new Task();

/** 创建4个线程,同时启动 */
/**
* Thread A
*/
new Thread(new Runnable() {

@Override
public void run() {
for (int i = 0; i < 10; i++) {
task.outputA();
}
}
}, " Thread A").start();

/**
* Thread B
*/
new Thread(new Runnable() {

@Override
public void run() {
for (int i = 0; i < 10; i++) {
task.outputB();
}
}
}, "Thread B").start();

/**
* Thread C
*/
new Thread(new Runnable() {

@Override
public void run() {
for (int i = 0; i < 10; i++) {
task.outputC();
}
}
}, "Thread C").start();

/**
* Thread D
*/
new Thread(new Runnable() {

@Override
public void run() {
for (int i = 0; i < 10; i++) {
task.outputD();
}
}
}, "Thread D").start();
}

/**
任务类
*/
public static class Task {
/**
创建一个Lock锁对象
*/
private Lock lock = new ReentrantLock();

private BufferedWriter bw1 = null;
private BufferedWriter bw2 = null;
private BufferedWriter bw3 = null;
private BufferedWriter bw4 = null;

private int ctl = 0;

/**
* Condition: 线程条件
*/
private Condition cond1 = lock.newCondition();
private Condition cond2 = lock.newCondition();
private Condition cond3 = lock.newCondition();
private Condition cond4 = lock.newCondition();

private boolean[] outputThisRound = { false, true, true, true };

public Task() {
try {
bw1 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
"/Users/luolong/Code/Android/workspace/MultiThreads/A.txt"))));
bw2 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
"/Users/luolong/Code/Android/workspace/MultiThreads/B.txt"))));
bw3 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
"/Users/luolong/Code/Android/workspace/MultiThreads/C.txt"))));
bw4 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
"/Users/luolong/Code/Android/workspace/MultiThreads/D.txt"))));
} catch (Exception e) {
e.printStackTrace();
}
}

public void outputA() {

lock.lock();

try {
while (outputThisRound[0]) {
System.out.println("outputA begin to await!");
cond1.await(); //阻塞A线程
}

if (ctl % 4 == 0) {
bw1.write("A");
bw1.flush();
} else if (ctl % 4 == 1) {
bw4.write("A");
bw4.flush();
} else if (ctl % 4 == 2) {
bw3.write("A");
bw3.flush();
} else if (ctl % 4 == 3) {
bw2.write("A");
bw2.flush();
}

outputThisRound[0] = true;
outputThisRound[1] = false;

System.out.println("outputA signal outputB!");
cond2.signal(); //唤醒B线程
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}

}

public void outputB() {

lock.lock();

try {
while (outputThisRound[1]) {
System.out.println("outputB begin to await!");
cond2.await();
}

if (ctl % 4 == 0) {
bw2.write("B");
bw2.flush();
} else if (ctl % 4 == 1) {
bw1.write("B");
bw1.flush();
} else if (ctl % 4 == 2) {
bw4.write("B");
bw4.flush();
} else if (ctl % 4 == 3) {
bw3.write("B");
bw3.flush();
}

outputThisRound[1] = true;
outputThisRound[2] = false;
System.out.println("outputB signal outputC!");
cond3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void outputC() {
lock.lock();
try {
while (outputThisRound[2]) {
System.out.println("outputC begin to await!");
cond3.await();
}

if (ctl % 4 == 0) {
bw3.write("C");
bw3.flush();
} else if (ctl % 4 == 1) {
bw2.write("C");
bw2.flush();
} else if (ctl % 4 == 2) {
bw1.write("C");
bw1.flush();
} else if (ctl % 4 == 3) {
bw4.write("C");
bw4.flush();
}

outputThisRound[2] = true;
outputThisRound[3] = false;
System.out.println("outputC signal outputD!");
cond4.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void outputD() {
lock.lock();

try {
while (outputThisRound[3]) {
System.out.println("outputD begin to await!");
cond4.await();
}

if (ctl % 4 == 0) {
bw4.write("D");
bw4.flush();
} else if (ctl % 4 == 1) {
bw3.write("D");
bw3.flush();
} else if (ctl % 4 == 2) {
bw2.write("D");
bw2.flush();
} else if (ctl % 4 == 3) {
bw1.write("D");
bw1.flush();
}

outputThisRound[3] = true;
outputThisRound[0] = false;
ctl++;
System.out.println("outputD signal outputA!");
cond1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}

以上。

如果大家还有其他更好的方法,欢迎一起讨论:-)

Created by Long Luo at 2015-04-09 22:14:02 @Shenzhen, China. Completed By Long Luo at 2015-04-09 23:46:29 @Shenzhen, China.

By Long Luo

最近去面试时,在一家小公司面试时,公司小BOSS给我出了一道算法题:

一个人爬楼梯,一步可以迈一级,二级,三级台阶,如果楼梯有 \(N\) 级,要求编写程序,求总共有多少种走法。

这个问题应该是一个很老的题目了,用中学数学来说,就是一个排列组合问题。当时拿到这个题目之后,首先想到使用递归的思想去解决这个问题:

N级楼梯问题可以划分为:\(N-1\) 级楼梯,\(N-2\) 级楼梯,\(N-3\) 级楼梯的走法之和。

先计算下0,1,2,3及楼梯有多少种走法:

1
2
3
1 --> 1
2 --> 11 2
3 --> 111 12 21 3

那么,根据以上的分析很容易写出如下代码:

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
public static int countNumber(int stepsNum) {
int sum = 0;

if (stepsNum == 0) {
return 0;
}

if (stepsNum == 1) {
return 1;
} else if (stepsNum == 2) {
return 2;
} else if (stepsNum == 3) {
return 4;
} else if (stepsNum > 3) {
return countNumber(stepsNum - 3) + countNumber(stepsNum - 2)
+ countNumber(stepsNum - 1);
}

return sum;
}

public static void main(String[] args) {

for (int i = 0; i <= 10; i++) {
System.out.println("楼梯台阶数:" + i + ", 走法有:" + countNumber(i));
}
}

再看看输出:

1
2
3
4
5
6
7
8
9
10
楼梯台阶数:0, 走法有:0
楼梯台阶数:1, 走法有:1
楼梯台阶数:2, 走法有:2
楼梯台阶数:3, 走法有:4
楼梯台阶数:4, 走法有:7
楼梯台阶数:5, 走法有:13
楼梯台阶数:6, 走法有:24
楼梯台阶数:7, 走法有:44
楼梯台阶数:8, 走法有:81
楼梯台阶数:9, 走法有:149

如果求解具体全部走法呢?

仅仅算出有多少种走法是很容易的,如果更近一步呢?基于这个基础,如何输出具体的走法呢?

我们可以使用Stack数据结构和递归的思想去完成这个题目:Stack<T>用于保存每一步的走法。

代码如下所示:

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
/**
* 一个人爬楼梯,一步可以迈一级,二级,三级台阶,如果楼梯有N级,编写程序,输出所有走法。
*
* @param args
*/
public static void main(String[] args) {
Stack<Integer> stt = new Stack<Integer>();

buileT(stt, 3);
}

public static void buileT(Stack<Integer> stt, int N) {
if (N >= 1) {
stt.push(1);
buileT(stt, N - 1);
stt.pop();
}
if (N >= 2) {
stt.push(2);
buileT(stt, N - 2);
stt.pop();
}
if (N >= 3) {
stt.push(3);
buileT(stt, N - 3);
stt.pop();
}
if (N == 0) {
for (int i : stt) {
System.out.print("Step:" + i + "-->");
}
System.out.println("完成");
}
}

以上。

———- Updated at 2022.04.21 ———-

这篇文章写于2015年,其实这个题就是 Leetcode 1137. 第 N 个泰波那契数 ,是 70. 爬楼梯 的变体,解决方法可参考 9种求斐波那契数(Fibonacci Numbers)的算法

Created by Long Luo at 2015-04-08 00:40:12 @Shenzhen, China. Completed By Long Luo at 2015-04-08 18:15:38 @Shenzhen, China. Updated By Long Luo at 2022年4月21日 14点51分 @Shenzhen, China.

0%