diff --git a/2017/05/19/2017-5-19-javacc-minilisp/index.html b/2017/05/19/2017-5-19-javacc-minilisp/index.html index 7a69ea13..1d5efc70 100644 --- a/2017/05/19/2017-5-19-javacc-minilisp/index.html +++ b/2017/05/19/2017-5-19-javacc-minilisp/index.html @@ -30,8 +30,8 @@ - + @@ -305,8 +305,8 @@
1 | string : num ':' {CHAR}* |
翻译为eBNF语法呢,就是如下
+1 | string : num ':' {CHAR}* |
根据BNF实现的解码代码如下, 把get_content()
方法中path替换为种子文件的路径,运行就可以看到。
返回的解析结果中会有info_hash
,该值是根据info的bencoding的二进制串计算的sha1值。这个值很重要
diff --git a/2023/01/12/2023-1-12-8-queens-chatgpt/index.html b/2023/01/12/2023-1-12-8-queens-chatgpt/index.html
index 9f75e3da..437ceb22 100644
--- a/2023/01/12/2023-1-12-8-queens-chatgpt/index.html
+++ b/2023/01/12/2023-1-12-8-queens-chatgpt/index.html
@@ -32,8 +32,8 @@
-
+
@@ -271,8 +271,8 @@
以下Python代码实现对Excel
转存的csv文件进行读取。
import pandas as pd |
把csv
文件入库是一件脏活。表面上看csv
文件是一个非常简单的
-逗号分隔符文件。但是其实不然。Excel
转存的csv
文件并不是标准的以逗号作为分隔符,
-并且对所有的项用双引号包裹。现在我就遇到了从Oracle
导出的csv
文件,以上的代码
-不起作用了。
究竟怎么回事呢,找了一圈也没有发现使用pandas.read_csv()
读取
-这种标准csv文件的方法。还是先把问题简化一下,看看Python
的csv模块
是如何读取的吧。
-简单的查找就可以找到答案。
import csv |
好的,pandas.read_csv()
肯定是要调用csv模块的,那么看看
-它的方法参数表吧。
--pandas.read_csv(filepath_or_buffer, sep=’, ‘, delimiter=None, header=’infer’, names=None, index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False, date_parser=None, dayfirst=False, iterator=False, chunksize=None, compression=’infer’, thousands=None, decimal=’.’, lineterminator=None, quotechar=’”‘, quoting=0, escapechar=None, comment=None, encoding=None, dialect=None, tupleize_cols=False, error_bad_lines=True, warn_bad_lines=True, skipfooter=0, skip_footer=0, doublequote=True, delim_whitespace=False, as_recarray=False, compact_ints=False, use_unsigned=False, low_memory=True, buffer_lines=None, memory_map=False, float_precision=None)[source]¶
-
真是够长的,还是搜一下有没有dialect=
,呵呵,果然有。
--dialect : str or csv.Dialect instance, default None
-
- If None defaults to Excel dialect. Ignored if sep longer than 1 char See csv.Dialect documentation for more details
那么问题解决了。把第一段代码改改吧。
-import pandas as pd |
一运行还是报错,这是怎么回事呢,编码换成utf8
,也不行。最后才发现
-需要使用gb18030
才行。即使使用chardet
的编码探测模块,也不一定能探测出来,因为整个文档
-只有少数字符是超出了gbk
,所以不可能既高效又准确的解决这个问题。
以下Python代码实现对Excel
转存的csv文件进行读取。
import pandas as pd |
把csv
文件入库是一件脏活。表面上看csv
文件是一个非常简单的
+逗号分隔符文件。但是其实不然。Excel
转存的csv
文件并不是标准的以逗号作为分隔符,
+并且对所有的项用双引号包裹。现在我就遇到了从Oracle
导出的csv
文件,以上的代码
+不起作用了。
究竟怎么回事呢,找了一圈也没有发现使用pandas.read_csv()
读取
+这种标准csv文件的方法。还是先把问题简化一下,看看Python
的csv模块
是如何读取的吧。
+简单的查找就可以找到答案。
import csv |
好的,pandas.read_csv()
肯定是要调用csv模块的,那么看看
+它的方法参数表吧。
++pandas.read_csv(filepath_or_buffer, sep=’, ‘, delimiter=None, header=’infer’, names=None, index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False, date_parser=None, dayfirst=False, iterator=False, chunksize=None, compression=’infer’, thousands=None, decimal=’.’, lineterminator=None, quotechar=’”‘, quoting=0, escapechar=None, comment=None, encoding=None, dialect=None, tupleize_cols=False, error_bad_lines=True, warn_bad_lines=True, skipfooter=0, skip_footer=0, doublequote=True, delim_whitespace=False, as_recarray=False, compact_ints=False, use_unsigned=False, low_memory=True, buffer_lines=None, memory_map=False, float_precision=None)[source]¶
+
真是够长的,还是搜一下有没有dialect=
,呵呵,果然有。
++dialect : str or csv.Dialect instance, default None
+
+ If None defaults to Excel dialect. Ignored if sep longer than 1 char See csv.Dialect documentation for more details
那么问题解决了。把第一段代码改改吧。
+import pandas as pd |
一运行还是报错,这是怎么回事呢,编码换成utf8
,也不行。最后才发现
+需要使用gb18030
才行。即使使用chardet
的编码探测模块,也不一定能探测出来,因为整个文档
+只有少数字符是超出了gbk
,所以不可能既高效又准确的解决这个问题。
Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
-
-Example 1:
-
-11110
-11010
-11000
-00000
-Answer: 1
-
-Example 2:
+ [leetcode 282]Expression Add Operators 原创解法
+ /2016/12/19/leetcode-282/
+ 题目概述
+Given a string that contains only digits 0-9 and a target value, return all possibilities to add binary operators (
+not unary) +, -, or * between the digits so they evaluate to the target value.
-11000
-11000
-00100
-00011
-Answer: 3
+ "123", 6 -> ["1+2+3", "1*2*3"]
+ "232", 8 -> ["2*3+2", "2+3*2"]
+ "105", 5 -> ["1*0+5","10-5"]
+ "00", 0 -> ["0+0", "0-0", "0*0"]
+ "3456237490", 9191 -> []
- 最终解法
解法的代码先贴出来了,解题思路后补啊。总体来说,就是一行一行的扫描。
-是个不错的在线算法
,也就是说如果数据量非常大也没关系,呵呵。
-class Solution(object):
def numIslands(self, grid):
"""
基本思路,一行一行的扫描。
:type grid: List[List[str]]
:rtype: int
"""
print("====")
n = len(grid)
if n == 0: return 0
length = len(grid[0])
h_pre = {} # key : 是 “i,j” 字符串。value =》 [accumulator] ,一个list包含了岛的索引。
h_curr = {} # 当前行
a = [] # 保存的是一个个的list,list的长度是1,值对应岛的索引。
debug = []
accumulator = 1 # 表示岛的自增索引,每当发现一个新的岛,自增。
for i in range(0, length):
if i == 0 and grid[0][i] == '1':
h_curr['0,' + str(i)] = [accumulator]
a.append(h_curr['0,' + str(i)])
debug.append('0,' + str(i))
elif i > 0 and grid[0][i] == '1':
if grid[0][i - 1] == '1':
h_curr[str(0) + ',' + str(i)] = h_curr[str(0) + ',' + str(i - 1)]
else:
accumulator += 1
h_curr['0,' + str(i)] = [accumulator]
a.append(h_curr['0,' + str(i)])
debug.append('0,' + str(i))
for i in range(1, n):
h_pre = h_curr
h_curr = {}
for j in range(0, length):
if j == 0 and grid[i][j] == '1':
if grid[i - 1][j] == '1':
h_curr[str(i) + ',' + str(j)] = h_pre[str(i - 1) + ',' + str(j)]
else:
accumulator += 1
h_curr[str(i) + ',' + str(j)] = [accumulator]
a.append(h_curr[str(i) + ',' + str(j)])
debug.append(str(i) + ',' + str(j))
elif j > 0 and grid[i][j] == '1':
if grid[i - 1][j] == '1' and grid[i][j - 1] == '1':
pre = h_curr[str(i) + ',' + str(j - 1)]
above = h_pre[str(i - 1) + ',' + str(j)]
if pre[0] == above[0]:
# 对pre的值进行更新
h_curr[str(i) + ',' + str(j)] = pre
else :
h_curr[str(i) + ',' + str(j)] = above
v1 = pre[0]
v2 = above[0] # z这里一定要换成静态的值,否则的话当a[k]的值进行更新时,会影响到pre的值。
for k in range(0, len(a)):
if a[k][0] == v1:
a[k][0] = v2
elif grid[i][j - 1] == '1' and grid[i - 1][j] != '1':
pre = h_curr[str(i) + ',' + str(j - 1)]
h_curr[str(i) + ',' + str(j)] = pre
elif grid[i][j - 1] != '1' and grid[i - 1][j] == '1':
above = h_pre[str(i - 1) + ',' + str(j)]
h_curr[str(i) + ',' + str(j)] = above
else:
accumulator += 1
h_curr[str(i) + ',' + str(j)] = [accumulator]
a.append(h_curr[str(i) + ',' + str(j)])
debug.append(str(i) + ',' + str(j))
s = set()
for item in a:
s.add(item[0])
return len(s)
if __name__ == "__main__":
grid = ["11000",
"11000",
"00100",
"00011"] # 3
print(Solution().numIslands(grid))
grid = ["10111",
"10101",
"11101"] # 1
print(Solution().numIslands(grid))
grid = ["1111111",
"0000001",
"1111101",
"1000101",
"1010101",
"1011101",
"1111111"] # 1
print(Solution().numIslands(grid))
grid = ["10011101100000000000",
"10011001000101010010",
"00011110101100001010",
"00011001000111001001",
"00000001110000000000",
"10000101011000000101",
"00010001010101010101",
"00010100110101101110",
"00001001100001000101",
"00100100000100100010",
"10010000000100101010",
"01000101011011101100",
"11010000100000010001",
"01001110001111101000",
"00111000110001010000",
"10010100001000101011",
"10100000010001010000",
"01100011101010111100",
"01000011001010010011",
"00000011110100011000"] # 58
print(Solution().numIslands(grid))
+首先想到的解法就是遍历所有的组合,一一计算比较排除。
+class Solution1(object):
def addOperators(self, num, target):
"""
:type num: str
:type target: int
:rtype: List[str]
"""
if num:
result = []
path = []
n = len(num)
self.find(num, 0, n, target, path, result)
return result
def find(self, num, begin, end, target, path, result):
if begin == end:
s = "".join(path)
if self.is_valid(path) and eval(s) == target:
result.append(s)
else:
path.append(num[begin])
self.find(num, begin + 1, end, target, path, result)
if begin < end - 1:
path.append("+")
self.find(num, begin + 1, end, target, path, result)
path.pop()
path.append("-")
self.find(num, begin + 1, end, target, path, result)
path.pop()
path.append("*")
self.find(num, begin + 1, end, target, path, result)
path.pop()
path.pop()
def is_valid(self, path):
flag = True
tmp = ""
for i in path:
if i in "0123456789":
tmp += i
elif i in "+-*":
if len(tmp) >= 2 and tmp[0] == '0':
flag = False
break
tmp = ""
if len(tmp) > 1 and tmp[0] == "0":
flag = False
return flag
+
+ 分析
没有意外,这种解法超时了。
+先不考虑eval()函数是否应该使用。
+显然的情况是每一种字符组合至少遍历4遍。
+
+- 第一遍,得到组合
+- 第二遍,join
+- 第三遍,eval
+- 第四遍,is_valid排除
+
+有办法使用动态规划么?把一个大问题替换成子问题进行求解。
+比如先计算一个字符的所有的解。然后考虑增加到两个字符。在第一个字符的解
+的基础上,考虑在两个字符中间插入加减乘。然后再在两个字符的解的基础上
+再加入第三个字符。以此类推。这种方法,并没有减少计算量,其实还是遍历
+所有的组合。然后得到最终的解。
+那么最终我们可以选择的优化方向就是,使用深度优先遍历的时候,必须要在
+遍历的同时,进行计算。这样当到达结束判断的时候,能够直接判断。
+ 简化问题
为了便于分析,首先简化问题。考虑如何计算字符串”123”,并且
+只允许插入加号或者不插入加号。
+为了把问题组合描述为一个树的形式。我们把两个数字直接连接起来组成
+一个新的数字,看做一个操作,定义为.
,那么a.b = ab
,而数值计算
+的公式为a.b = a*10+b
,
+而且这个符号的优先级高于加号和乘号。
+这样可得如下树图。
+
+根据这种假定,对于任意数字字符串n1n2n3
,可以得到
+的查找树如下图。
+
+ 通过的解法
这样我们有了解法2。
+class Solution2(object):
def addOperators(self, num, target):
"""
这个居然通过了。呵呵。不过是勉强通过的。
:type num: str
:type target: int
:rtype: List[str]
"""
if num:
end = len(num)
result = []
path = [num[0]]
s1 = [int(num[0])]
s2 = []
begin = 1
self.find(begin, end, num, s1, s2, path, target, result)
return result
else:
return []
def find(self, begin, end, num, s1, s2, path, target, result):
if begin == end:
if s1[0] == target:
result.append("".join(path))
else:
ops = [("+", 10), ("-", 10), ("*", 20), (".", 30)]
ps = ["+", "-", "*", ""]
if begin + 1 < end:
for i in range(4):
s11 = s1[:]
s22 = s2[:]
path.append(ps[i] + num[begin])
self.helper(begin, end, num, ops[i], path, result, s11, s22, target)
path.pop()
elif begin + 1 == end:
for i in range(4):
s11 = s1[:]
s22 = s2[:]
path.append(ps[i] + num[begin])
self.helper2(begin, end, num, ops[i], path, result, s11, s22, target)
path.pop()
def helper(self, begin, end, num, op, path, result, s1, s2, target):
# 没有到达结尾
if s2:
op_pre = s2[-1]
if op_pre[1] < op[1]:
# 需要压栈延迟计算
s1.append(int(num[begin]))
s2.append(op)
self.find(begin + 1, end, num, s1, s2, path, target, result)
else:
# 需要先把s2出栈计算完成,直到栈中的符号级别小于当前。
is_valid = True
while s2:
op_pre = s2[-1]
if op_pre[1] < op[1]:
break
op_pre = s2.pop()
n2 = s1.pop()
n1 = s1[-1]
if op_pre[0] == "+":
s1[-1] = n1 + n2
elif op_pre[0] == "-":
s1[-1] = n1 - n2
elif op_pre[0] == "*":
s1[-1] = n1 * n2
elif op_pre[0] == "." and n1 != 0:
s1[-1] = n1 * 10 + n2
else:
is_valid = False
break
if is_valid:
s1.append(int(num[begin]))
s2.append(op)
self.find(begin + 1, end, num, s1, s2, path, target, result)
else:
s1.append(int(num[begin]))
s2.append(op)
self.find(begin + 1, end, num, s1, s2, path, target, result)
def helper2(self, begin, end, num, op, path, result, s1, s2, target):
# 到达结尾
if s2:
op_pre = s2[-1]
if op_pre[1] < op[1]:
s1.append(int(num[begin]))
s2.append(op)
is_valid = True
while s2:
op_pre = s2.pop()
n2 = s1.pop()
n1 = s1[-1]
if op_pre[0] == "+":
s1[-1] = n1 + n2
elif op_pre[0] == "-":
s1[-1] = n1 - n2
elif op_pre[0] == "*":
s1[-1] = n1 * n2
elif op_pre[0] == "." and n1 != 0:
s1[-1] = n1 * 10 + n2
else:
is_valid = False
break
if is_valid:
self.find(begin + 1, end, num, s1, s2, path, target, result)
else:
is_valid = True
while s2:
op_pre = s2[-1]
if op_pre[1] < op[1]:
break
op_pre = s2.pop()
n2 = s1.pop()
n1 = s1[-1]
if op_pre[0] == "+":
s1[-1] = n1 + n2
elif op_pre[0] == "-":
s1[-1] = n1 - n2
elif op_pre[0] == "*":
s1[-1] = n1 * n2
elif op_pre[0] == "." and n1 != 0:
s1[-1] = n1 * 10 + n2
else:
is_valid = False
break
if is_valid:
s2.append(op)
s1.append(int(num[begin]))
while s2:
op_pre = s2.pop()
n2 = s1.pop()
n1 = s1[-1]
if op_pre[0] == "+":
s1[-1] = n1 + n2
elif op_pre[0] == "-":
s1[-1] = n1 - n2
elif op_pre[0] == "*":
s1[-1] = n1 * n2
elif op_pre[0] == "." and n1 != 0:
s1[-1] = n1 * 10 + n2
else:
is_valid = False
break
if is_valid:
self.find(begin + 1, end, num, s1, s2, path, target, result)
else:
n2 = int(num[begin])
n1 = s1[-1]
is_valid = True
if op[0] == "+":
s1[-1] = n1 + n2
elif op[0] == "-":
s1[-1] = n1 - n2
elif op[0] == "*":
s1[-1] = n1 * n2
elif op[0] == "." and n1 != 0:
s1[-1] = n1 * 10 + n2
else:
is_valid = False
if is_valid:
self.find(begin + 1, end, num, s1, s2, path, target, result)
]]>
leetcode
@@ -427,96 +393,19 @@ Answer: 3
Cython
不是CPython
的简称,而是一种提升Python
代码执行效率的解决方案。
-据说一般达到30x。一会我们来看看是不是真的。这个技术对老鸟来说已经是好多
-年前的了。但是很多情况下python用户真的用不上,所以不知道也无妨。
从python3.5
开始,使用venv
创建虚拟环境已经成为官方推荐的方案。
+查看官方文档,可以看到如果想要创建一个干净的虚拟环境。方式如下
python -m venv myenv
+
-我已经迁移到python3
了,使用的是Anaconda
。感谢Anaconda
让我们的生活变
-的更加美好。以下软件安装的顺序不要错。
先创建一个helloworld.pyx文件。内容如下。
-# helloworld.pyx |
再创建一个setup.py文件。内容如下
-#setup.py |
然后在命令行输入
-D:\tmp\oo>python setup.py build_ext --inplace
-running build_ext
-building 'helloworld' extension
-C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -IC:\Anaconda3\include -IC:\Anaconda3\include "-IC:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\shared" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\winrt" /Tchelloworld.c /Fobuild\temp.win-amd64-3.5\Release\helloworld.obj
-helloworld.c
-C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:C:\Anaconda3\libs /LIBPATH:C:\Anaconda3\PCbuild\amd64 "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\amd64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.10240.0\ucrt\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.10240.0\um\x64" /EXPORT:PyInit_helloworld build\temp.win-amd64-3.5\Release\helloworld.obj /OUT:D:\tmp\oo\helloworld.cp35-win_amd64.pyd /IMPLIB:build\temp.win-amd64-3.5\Release\helloworld.cp35-win_amd64.lib
-helloworld.obj : warning LNK4197: export 'PyInit_helloworld' specified multiple times; using first specification
- Creating library build\temp.win-amd64-3.5\Release\helloworld.cp35-win_amd64.lib and object build\temp.win-amd64-3.5\Release\helloworld.cp35-win_amd64.exp
-Generating code
-Finished generating code
-
-(WIN10下的命令窗口终于可以使用ctrl-c和ctrl-v了。泪奔。)
-可以发现多了一个.pyd
文件。然后测试一下。
D:\tmp\oo>python
-Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul 5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)] on win32
-Type "help", "copyright", "credits" or "license" for more information.
->>> import helloworld as h
-hello cython!
->>>h.fib(3)
-3
-
-真是不错啊。可是如果你换一个目录比如不在当前包含.pyd
的目录下,
-再想导入helloworld则是不行的。怎么回事,我们不是已经setup了么。
-原因在于--inplace
这个参数,它表示只生成动态链接库。
我们还在那个目录下创建一个test.py
文件。
#test.py |
然后运行
-D:\tmp\oo>python test.py
-python fib : 0.05887972s
-hello cython!
-cython fib : 0.01739922s
-
-怎么回事,说好的30x呢,还轻轻松松?别着急,我们把之前的helloworld.pyx
-修改一下。
#helloworld.pyx |
再运行一下瞅瞅。
-D:\tmp\oo>python test.py
-python fib : 0.05774631s
-hello cython!
-cython fib : 0.00059495s
-
-真是不错哦,诚不我欺,100x。
-]]>从python3.5
开始,使用venv
创建虚拟环境已经成为官方推荐的方案。
-查看官方文档,可以看到如果想要创建一个干净的虚拟环境。方式如下
python -m venv myenv
-
-
-
-
-
-如果要创建一个当前环境的副本,也就是说,可以引用当前环境的所有已经
-安装的包。那么需要增加--system-site-packages
参数
python -m venv myenv2 --system-site-packages
+如果要创建一个当前环境的副本,也就是说,可以引用当前环境的所有已经
+安装的包。那么需要增加--system-site-packages
参数
+python -m venv myenv2 --system-site-packages
两种方式,我们都希望能够独立使用pip来管理包。但是第二种方式发现并没有pip
文件。
@@ -685,6 +574,40 @@ coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
的min改成max。如果这两个问题是等价的,那么我们的解法一定可以通过测试。并且似乎也不用去证明了。(其实这是最难的部分。)
以下是通过的memoization
解法,算法的复杂度是O(n^2),与矩阵链问题的复杂度一样:
class Solution2(object):
def maxCoins(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
n = len(nums)
list = self.get(nums) # 转换为n+1个矩阵的表示。每个pair tuple对应矩阵的长和宽。
print(list)
n += 1
m = [[None] * n for i in range(n)] # 声明一个子问题空间是n^2的记忆体
print(m)
for i in range(n):
m[i][i] = 0
return self.recursive(list, m, 0, n - 1)
def get(self, nums):
n = len(nums)
list = [(1, nums[0])]
for i in range(n):
if (i + 1 < n):
list.append((nums[i], nums[i + 1]))
else:
list.append((nums[i], 1))
return list
def recursive(self, list, m, i, j):
if m[i][j] is not None:
return m[i][j]
else:
max = -1
for k in range(i, j):
a = self.recursive(list, m, i, k)
b = self.recursive(list, m, k + 1, j)
v = a + b + list[i][0] * list[k][1] * list[j][1]
if v > max :
max = v
m[i][j] = max
return m[i][j]
+]]>
Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
+
+Example 1:
+
+11110
+11010
+11000
+00000
+Answer: 1
+
+Example 2:
+
+11000
+11000
+00100
+00011
+Answer: 3
+
+
+
+解法的代码先贴出来了,解题思路后补啊。总体来说,就是一行一行的扫描。
+是个不错的在线算法
,也就是说如果数据量非常大也没关系,呵呵。
class Solution(object): |
Cython
不是CPython
的简称,而是一种提升Python
代码执行效率的解决方案。
+据说一般达到30x。一会我们来看看是不是真的。这个技术对老鸟来说已经是好多
+年前的了。但是很多情况下python用户真的用不上,所以不知道也无妨。
我已经迁移到python3
了,使用的是Anaconda
。感谢Anaconda
让我们的生活变
+的更加美好。以下软件安装的顺序不要错。
先创建一个helloworld.pyx文件。内容如下。
+# helloworld.pyx |
再创建一个setup.py文件。内容如下
+#setup.py |
然后在命令行输入
+D:\tmp\oo>python setup.py build_ext --inplace
+running build_ext
+building 'helloworld' extension
+C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -IC:\Anaconda3\include -IC:\Anaconda3\include "-IC:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\shared" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\winrt" /Tchelloworld.c /Fobuild\temp.win-amd64-3.5\Release\helloworld.obj
+helloworld.c
+C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:C:\Anaconda3\libs /LIBPATH:C:\Anaconda3\PCbuild\amd64 "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\amd64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.10240.0\ucrt\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.10240.0\um\x64" /EXPORT:PyInit_helloworld build\temp.win-amd64-3.5\Release\helloworld.obj /OUT:D:\tmp\oo\helloworld.cp35-win_amd64.pyd /IMPLIB:build\temp.win-amd64-3.5\Release\helloworld.cp35-win_amd64.lib
+helloworld.obj : warning LNK4197: export 'PyInit_helloworld' specified multiple times; using first specification
+ Creating library build\temp.win-amd64-3.5\Release\helloworld.cp35-win_amd64.lib and object build\temp.win-amd64-3.5\Release\helloworld.cp35-win_amd64.exp
+Generating code
+Finished generating code
+
+(WIN10下的命令窗口终于可以使用ctrl-c和ctrl-v了。泪奔。)
+可以发现多了一个.pyd
文件。然后测试一下。
D:\tmp\oo>python
+Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul 5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)] on win32
+Type "help", "copyright", "credits" or "license" for more information.
+>>> import helloworld as h
+hello cython!
+>>>h.fib(3)
+3
+
+真是不错啊。可是如果你换一个目录比如不在当前包含.pyd
的目录下,
+再想导入helloworld则是不行的。怎么回事,我们不是已经setup了么。
+原因在于--inplace
这个参数,它表示只生成动态链接库。
我们还在那个目录下创建一个test.py
文件。
#test.py |
然后运行
+D:\tmp\oo>python test.py
+python fib : 0.05887972s
+hello cython!
+cython fib : 0.01739922s
+
+怎么回事,说好的30x呢,还轻轻松松?别着急,我们把之前的helloworld.pyx
+修改一下。
#helloworld.pyx |
再运行一下瞅瞅。
+D:\tmp\oo>python test.py
+python fib : 0.05774631s
+hello cython!
+cython fib : 0.00059495s
+
+真是不错哦,诚不我欺,100x。
+]]>Java世界里构建项目用什么工具呢,ant,maven和gradle。maven非常流行, +原因无非是maven仓库和项目架构的约定。但是ant构建过程更加容易理解, +因为ant展示了所有的操作。gradle拥有基于groovy语言的DSL语言且继承了 +maven仓库的思想所以笔者认为未来是属于gradle的。
+ + +特别是最近在搭建springmvc-jpa-mysql开发框架的时候,发现怎么也找不到一个 +archetype可以做到一键构建demo project。
+何谓一键构建呢,有人用maven也可以很快搭建一个,通常可以把之前的 +项目复制一份修改一下,似乎没有那么麻烦。但笔者还是觉得执行几个命令, +回车一下就构建完成,更加让人舒心。如图所示,创建一个空项目dir,然后把build.gradle +放入该路径下,执行
+gradle init
+gradle build -x test
+gradle run
+
+就可以在浏览器里访问http://localhost:8080/greeting?name=GoodJob
链接了。
+以下是过程展示。当然了因为jar包都已经下载过了,所以似乎还比较快。
+否则的话一定会联网下载jar包的。
+
以下导入IDEA中后所示的文件结构。 +
+build.gradle
构建基于spring-boot的springmvc-jpa-mysql框架demo项目的脚本如下。
+该脚本一目了然,无需赘述。
def createFile = { String path, String content -> |
比较流行的编程语言中,Java
,Python
都没有定义宏的功能。c
语言中的宏限制比较大,
+没有Lisp
中的宏强大。以下提到的宏,指的是Lisp方言中的宏。
宏的求值可以分为两个步骤
+这看起来很简单,但是实际写macro的时候,就会有很多的困惑。这里的主要矛盾就是理解 +以下两个过程,
+不同的lisp方言有不同的实现。以下用Clojure来研究宏的用法。
+ + +宏扩展阶段,就会调用函数进行求值。
+(use 'clojure.walk) |
运行结果如下,可以看到宏扩展阶段已经调用+
函数进行了求值。
#'user/fool |
这里的+
是一个函数。
继续上面的例子,我们定义一个加法宏,然后在fool的内部调用该宏。
+(use 'clojure.walk) |
运行这段代码会得到以下的错误信息。重点就是理解这里为什么会报错。
+CompilerException java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.lang.Number, compiling:(/tmp/form-init4487875798119154898.clj:8:3) |
这里报错说是Symbol无法转换为数字。可是我们明明输入的是2。
+事实的过程如下,
+所以,如果直接在一个宏的内部调用另一个宏,很可能编译都无法通过。因为编译期会对宏进行扩展, +但不会绑定实参。
+那么怎么正确的使用呢,这里引入了一个概念叫emit a macro call inside a macro
。
(use 'clojure.walk) |
结果如下,只扩展一次,会得到(plus 2 1)
, 而完全扩展会得到3
。
=> nil |
具体过程如下,
+正是因为需要频繁的使用list来进行宏调用,因此发明了这样的语法糖。
+(use 'clojure.walk) |
Java世界里构建项目用什么工具呢,ant,maven和gradle。maven非常流行, -原因无非是maven仓库和项目架构的约定。但是ant构建过程更加容易理解, -因为ant展示了所有的操作。gradle拥有基于groovy语言的DSL语言且继承了 -maven仓库的思想所以笔者认为未来是属于gradle的。
- - -特别是最近在搭建springmvc-jpa-mysql开发框架的时候,发现怎么也找不到一个 -archetype可以做到一键构建demo project。
-何谓一键构建呢,有人用maven也可以很快搭建一个,通常可以把之前的 -项目复制一份修改一下,似乎没有那么麻烦。但笔者还是觉得执行几个命令, -回车一下就构建完成,更加让人舒心。如图所示,创建一个空项目dir,然后把build.gradle -放入该路径下,执行
-gradle init
-gradle build -x test
-gradle run
-
-就可以在浏览器里访问http://localhost:8080/greeting?name=GoodJob
链接了。
-以下是过程展示。当然了因为jar包都已经下载过了,所以似乎还比较快。
-否则的话一定会联网下载jar包的。
-
以下导入IDEA中后所示的文件结构。 -
-build.gradle
构建基于spring-boot的springmvc-jpa-mysql框架demo项目的脚本如下。
-该脚本一目了然,无需赘述。
def createFile = { String path, String content -> |
比较流行的编程语言中,Java
,Python
都没有定义宏的功能。c
语言中的宏限制比较大,
-没有Lisp
中的宏强大。以下提到的宏,指的是Lisp方言中的宏。
宏的求值可以分为两个步骤
-这看起来很简单,但是实际写macro的时候,就会有很多的困惑。这里的主要矛盾就是理解 -以下两个过程,
-不同的lisp方言有不同的实现。以下用Clojure来研究宏的用法。
- - -宏扩展阶段,就会调用函数进行求值。
-(use 'clojure.walk) |
运行结果如下,可以看到宏扩展阶段已经调用+
函数进行了求值。
#'user/fool |
这里的+
是一个函数。
继续上面的例子,我们定义一个加法宏,然后在fool的内部调用该宏。
-(use 'clojure.walk) |
运行这段代码会得到以下的错误信息。重点就是理解这里为什么会报错。
-CompilerException java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.lang.Number, compiling:(/tmp/form-init4487875798119154898.clj:8:3) |
这里报错说是Symbol无法转换为数字。可是我们明明输入的是2。
-事实的过程如下,
-所以,如果直接在一个宏的内部调用另一个宏,很可能编译都无法通过。因为编译期会对宏进行扩展, -但不会绑定实参。
-那么怎么正确的使用呢,这里引入了一个概念叫emit a macro call inside a macro
。
(use 'clojure.walk) |
结果如下,只扩展一次,会得到(plus 2 1)
, 而完全扩展会得到3
。
=> nil |
具体过程如下,
-正是因为需要频繁的使用list来进行宏调用,因此发明了这样的语法糖。
-(use 'clojure.walk) |
-- - -Given a data stream input of non-negative integers a1, a2, …, an, …, summarize the numbers seen so far as a list of disjoint intervals.
-For example, suppose the integers from the data stream are 1, 3, 7, 2, 6, …, then the summary will be:
-[1, 1] -[1, 1], [3, 3] -[1, 1], [3, 3], [7, 7] -[1, 3], [7, 7] -[1, 3], [6, 7]
-
断断续续思考了一段时间。naive的想法考虑了以下几点,
-从这几点首先想到用二叉搜索树来存放interval,这样既能快速查到,又能保证有序, -但是实现起来的时候,发现有两个问题,
-最后考虑的结果是这样的,
-编码的过程,比较慢,写了快一个小时,因为要仔细考虑边界值的问题,
-但是一遍就通过了测试,比较开心,毕竟该题号称hard
。
# Definition for an interval. |
我一开始不愿意使用list的本质原因是需要在List的中间删除和插入数据,因为 -对于一个size为n的list,list.pop(0)的时间复杂度是O(n)。 -但是Python的List似乎仍然非常高效,即使在list中间删除和插入数。我本来以为 -我的这个解法会超时呢,结果居然击败的80%的提交。
-]]>The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the "root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that "all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses were broken into on the same night.
+Determine the maximum amount of money the thief can rob tonight without alerting the police.
+
+Example 1: |
以下代码打败了100%的其它python3解法。 +带记忆体的递归,也就是动态规划。
+class Solution: |
You are Here! |
The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the "root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that "all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses were broken into on the same night.
-Determine the maximum amount of money the thief can rob tonight without alerting the police.
-
-Example 1: |
+-Given a data stream input of non-negative integers a1, a2, …, an, …, summarize the numbers seen so far as a list of disjoint intervals.
+For example, suppose the integers from the data stream are 1, 3, 7, 2, 6, …, then the summary will be:
+[1, 1] +[1, 1], [3, 3] +[1, 1], [3, 3], [7, 7] +[1, 3], [7, 7] +[1, 3], [6, 7]
+
以下代码打败了100%的其它python3解法。 -带记忆体的递归,也就是动态规划。
-class Solution: |
断断续续思考了一段时间。naive的想法考虑了以下几点,
+从这几点首先想到用二叉搜索树来存放interval,这样既能快速查到,又能保证有序, +但是实现起来的时候,发现有两个问题,
+最后考虑的结果是这样的,
+编码的过程,比较慢,写了快一个小时,因为要仔细考虑边界值的问题,
+但是一遍就通过了测试,比较开心,毕竟该题号称hard
。
# Definition for an interval. |
You are Here! |
我一开始不愿意使用list的本质原因是需要在List的中间删除和插入数据,因为 +对于一个size为n的list,list.pop(0)的时间复杂度是O(n)。 +但是Python的List似乎仍然非常高效,即使在list中间删除和插入数。我本来以为 +我的这个解法会超时呢,结果居然击败的80%的提交。
]]>BEP3的peer message章节说明了几种消息类型。 +其中bitfield消息理解起来是容易的,但是实际过程中却略有不同。
+ + +++‘bitfield’ is only ever sent as the first message. Its payload is a bitfield with each index that downloader has sent set to one and the rest set to zero. Downloaders which don’t have anything yet may skip the ‘bitfield’ message. The first byte of the bitfield corresponds to indices 0 - 7 from high bit to low bit, respectively. The next one 8-15, etc. Spare bits at the end are set to zero.
+
这段话的意思很简单,举例说明一下,如果一个文件有10个分片,那么如果没有下载任何数据,那么bitfield的值应该是(为了便于阅读每个字节的二进制码 以一个空格键隔开)
+0b0000 0000 0000
+
+如果下载了piece 0, 则值应该是
+0b1000 0000 0000
+
+如果全部下载完成,则应该是
+0b1111 1111 1100
+
+一共是10个1,表示10个piece。
+但是实际上呢,对于一个下载完成的文件,获取到的bitfield值是0b111111111100
么?
我们可以自己制作一个种子来验证一下。
+用uTorrent软件选择本地的一个文件制作一个种子文件。如下图所示
+ +该文件有110个片。
+我们读取代码如下
+# -*- coding: utf-8 -*- |
我们看到日志如下
+/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
+sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
+Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xab\x9c\xa8$G\x8euX\x91\xad\x9e')
+id=20
+recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
+id=5
+recv=b'\xff\xff\xffg~\xaf\xbf=\x7f\xff\xeb&\xdd\x98'
+1111111111111111111111110110011101111110101011111011111100111101011111111111111111101011001001101101110110011000
+
+这是怎么回事,为什么中间会有这么多0呢,不是已经下载完成了么?
+111111111111111111111111011001110111111010101111101111110011110...
+
+应该是110个1才对。而且如果再运行几遍会发现每次有不同的index的片是0。
+原来这是uTorrent软件实现的一个策略,即使已经下载完成,也不会返回110个1的bitfield。而是用have消息
来
+补全,也就是id为4的peer消息。have消息的值是一个piece的index值,表示该piece下载完成。
因此,我们需要根据bitfield消息和have消息来看是否下载完成。代码修改一下,每接收到一个have消息就把相应的index +置为1。
+from ym.bt.peer_protocol import pack_handshake, unpack_handshake, sendall, recv, pack_extend |
我们再看一下日志
+/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
+sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
+Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xabY\x08\r\x82=\xd1J^\x930')
+id=20
+recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
+id=5
+recv=b'\xfb\xdf\xbf\xee\xdf\xdf\x9d\xef\xb9\xfa\xfbv^\xf8'
+1111101111011111101111111110111011011111110111111001110111101111101110011111101011111011011101100101111011111000
+id=4
+recv=b'\x00\x00\x002'
+index=50
+1111101111011111101111111110111011011111110111111011110111101111101110011111101011111011011101100101111011111000
+
+...
+...
+...
+
+id=4
+recv=b'\x00\x00\x00b'
+index=98
+1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111100
+id=4
+recv=b'\x00\x00\x00X'
+index=88
+1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
+id=4
+recv=b'\x00\x00\x00\n'
+index=10
+1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
+already recv:b''
+timed out
+Traceback (most recent call last):
+ File "/Users/ym/charm/pytest/ym/bt/peer_protocol.py", line 40, in recv
+ tmp = sock.recv(n - len(data))
+socket.timeout: timed out
+
+Process finished with exit code 0
+
+可以看到,确实是110个1。
+]]>BEP3的peer message章节说明了几种消息类型。 -其中bitfield消息理解起来是容易的,但是实际过程中却略有不同。
- - ---‘bitfield’ is only ever sent as the first message. Its payload is a bitfield with each index that downloader has sent set to one and the rest set to zero. Downloaders which don’t have anything yet may skip the ‘bitfield’ message. The first byte of the bitfield corresponds to indices 0 - 7 from high bit to low bit, respectively. The next one 8-15, etc. Spare bits at the end are set to zero.
-
这段话的意思很简单,举例说明一下,如果一个文件有10个分片,那么如果没有下载任何数据,那么bitfield的值应该是(为了便于阅读每个字节的二进制码 以一个空格键隔开)
-0b0000 0000 0000
-
-如果下载了piece 0, 则值应该是
-0b1000 0000 0000
-
-如果全部下载完成,则应该是
-0b1111 1111 1100
-
-一共是10个1,表示10个piece。
-但是实际上呢,对于一个下载完成的文件,获取到的bitfield值是0b111111111100
么?
我们可以自己制作一个种子来验证一下。
-用uTorrent软件选择本地的一个文件制作一个种子文件。如下图所示
- -该文件有110个片。
-我们读取代码如下
-# -*- coding: utf-8 -*- |
我们看到日志如下
-/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
-sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
-Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xab\x9c\xa8$G\x8euX\x91\xad\x9e')
-id=20
-recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
-id=5
-recv=b'\xff\xff\xffg~\xaf\xbf=\x7f\xff\xeb&\xdd\x98'
-1111111111111111111111110110011101111110101011111011111100111101011111111111111111101011001001101101110110011000
-
-这是怎么回事,为什么中间会有这么多0呢,不是已经下载完成了么?
-111111111111111111111111011001110111111010101111101111110011110...
-
-应该是110个1才对。而且如果再运行几遍会发现每次有不同的index的片是0。
-原来这是uTorrent软件实现的一个策略,即使已经下载完成,也不会返回110个1的bitfield。而是用have消息
来
-补全,也就是id为4的peer消息。have消息的值是一个piece的index值,表示该piece下载完成。
因此,我们需要根据bitfield消息和have消息来看是否下载完成。代码修改一下,每接收到一个have消息就把相应的index -置为1。
-from ym.bt.peer_protocol import pack_handshake, unpack_handshake, sendall, recv, pack_extend |
我们再看一下日志
-/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/bt_download.py
-sent=ok, data=b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x01\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5ym111111111111111111'
-Handshake(fixed_num=19, bt_head=b'BitTorrent protocol', reserved=b'\x00\x00\x00\x00\x00\x10\x00\x05', info_hash=b'\xc9\x9c;z[\xa3\x1a\x89f\xe6\xc9\xa4\x0b\xc4\xf88\x87\xa1\x07\xe5', peer_id=b'-UM1870-\x14\xabY\x08\r\x82=\xd1J^\x930')
-id=20
-recv=b'\x00d1:ei0e4:ipv616:\xfe\x80\x00\x00\x00\x00\x00\x00\x04:^:D[\x0c\x9212:complete_agoi1e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1e12:ut_recommendi5e10:ut_commenti6ee13:metadata_sizei2270e1:pi40959e4:reqqi255e1:v19:\xc2\xb5Torrent Mac 1.8.76:yourip4:\xc0\xa8+Re'
-id=5
-recv=b'\xfb\xdf\xbf\xee\xdf\xdf\x9d\xef\xb9\xfa\xfbv^\xf8'
-1111101111011111101111111110111011011111110111111001110111101111101110011111101011111011011101100101111011111000
-id=4
-recv=b'\x00\x00\x002'
-index=50
-1111101111011111101111111110111011011111110111111011110111101111101110011111101011111011011101100101111011111000
-
-...
-...
-...
-
-id=4
-recv=b'\x00\x00\x00b'
-index=98
-1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111100
-id=4
-recv=b'\x00\x00\x00X'
-index=88
-1111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
-id=4
-recv=b'\x00\x00\x00\n'
-index=10
-1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
-already recv:b''
-timed out
-Traceback (most recent call last):
- File "/Users/ym/charm/pytest/ym/bt/peer_protocol.py", line 40, in recv
- tmp = sock.recv(n - len(data))
-socket.timeout: timed out
-
-Process finished with exit code 0
-
-可以看到,确实是110个1。
-]]>bt通过种子文件分享已经是一个过去时了,2009年btChina
就已经关闭了。现在一般都是
-使用磁力链接来分享文件。那么为什么种子文件分享不再流行了呢?为什么要用磁力链接呢?
-磁力链接怎么实现的呢?
嗯这是这个系列要研究的问题。但是要研究磁力链接的实现原理,最好先从种子文件开始。
- - -官网文档 BEP3 中的metainfo files章节 -讲的很清楚。
-简单的说就是把tracker列表和分享的文件信息编码为一个二进制的文件。
-http://tracker.trackerfix.com:80/announce |
如果想要找到下载源,就要通过tracker找到peer节点。
-包含了文件的大小,分块个数,分块的sha1散列值。
-bt文件的编码逻辑取名为bencoding。
---Strings are length-prefixed base ten followed by a colon and the string. For example 4:spam corresponds to ‘spam’.
-Integers are represented by an ‘i’ followed by the number in base 10 followed by an ‘e’. For example i3e corresponds to 3 and i-3e corresponds to -3. Integers have no size limitation. i-0e is invalid. All encodings with a leading zero, such as i03e, are invalid, other than i0e, which of course corresponds to 0.
-Lists are encoded as an ‘l’ followed by their elements (also bencoded) followed by an ‘e’. For example l4:spam4:eggse corresponds to [‘spam’, ‘eggs’].
-Dictionaries are encoded as a ‘d’ followed by a list of alternating keys and their corresponding values followed by an ‘e’. For example, d3:cow3:moo4:spam4:eggse corresponds to {‘cow’: ‘moo’, ‘spam’: ‘eggs’} and d4:spaml1:a1:bee corresponds to {‘spam’: [‘a’, ‘b’]}. Keys must be strings and appear in sorted order (sorted as raw strings, not alphanumerics).
-
翻译为BNF语法呢,就是如下
-string : num ':' {CHAR}* |
根据BNF实现的解码代码如下, 把get_content()
方法中path替换为种子文件的路径,运行就可以看到。
-返回的解析结果中会有info_hash
,该值是根据info的bencoding的二进制串计算的sha1值。这个值很重要
-因为之后很多协议都会用到。
# -*- coding: utf-8 -*- |
用最近的毒液电影的种子进行解析,打印如下, -其中announce-list就是tracker列表。
-/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/parse_torrent.py |
K3s是什么?k8s的精简版。编译之后执行程序大小不到50M。 -可以用在物联网的边缘计算侧。如果想深入了解k8s,那么k3s是个很好的起点。 -那么如果能够断点调试k3s,就更好了。下面我们来看看怎么做。
- - -GOPATH
,同时把$GOPATH/bin
加入到PATH
$ git clone --depth 1 https://github.com/rancher/k3s.git $GOPATH/src/github.com/rancher/k3s |
$GOPATH/bin/dlv
$ git clone --depth 1 https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve |
k3s
,所在路径是$GOPATH/src/github.com/rancher/k3s/
$ cd $GOPATH/src/github.com/rancher/k3s |
$ cd $GOPATH/src/github.com/rancher/k3s |
$GOPATH
,然后配置增加一个remote调试。在运行之前,在main.go上打一个断点。运行远程调试之后,成功。 -
-]]>用info_hash过滤日志,可以看到metadata包含了一个files
项,该项包含了多个文件。
root@ubuntu:~# cat log.txt | grep "54730eeeb5d74a58f49c6da72eb90be922556f0a"| grep "metadata_dic" |
K3s是什么?k8s的精简版。编译之后执行程序大小不到50M。 +可以用在物联网的边缘计算侧。如果想深入了解k8s,那么k3s是个很好的起点。 +那么如果能够断点调试k3s,就更好了。下面我们来看看怎么做。
+ + +GOPATH
,同时把$GOPATH/bin
加入到PATH
$ git clone --depth 1 https://github.com/rancher/k3s.git $GOPATH/src/github.com/rancher/k3s |
$GOPATH/bin/dlv
$ git clone --depth 1 https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve |
k3s
,所在路径是$GOPATH/src/github.com/rancher/k3s/
$ cd $GOPATH/src/github.com/rancher/k3s |
$ cd $GOPATH/src/github.com/rancher/k3s |
$GOPATH
,然后配置增加一个remote调试。在运行之前,在main.go上打一个断点。运行远程调试之后,成功。 +
+]]>bt通过种子文件分享已经是一个过去时了,2009年btChina
就已经关闭了。现在一般都是
+使用磁力链接来分享文件。那么为什么种子文件分享不再流行了呢?为什么要用磁力链接呢?
+磁力链接怎么实现的呢?
嗯这是这个系列要研究的问题。但是要研究磁力链接的实现原理,最好先从种子文件开始。
+ + +官网文档 BEP3 中的metainfo files章节 +讲的很清楚。
+简单的说就是把tracker列表和分享的文件信息编码为一个二进制的文件。
+http://tracker.trackerfix.com:80/announce |
如果想要找到下载源,就要通过tracker找到peer节点。
+包含了文件的大小,分块个数,分块的sha1散列值。
+bt文件的编码逻辑取名为bencoding。
+++Strings are length-prefixed base ten followed by a colon and the string. For example 4:spam corresponds to ‘spam’.
+Integers are represented by an ‘i’ followed by the number in base 10 followed by an ‘e’. For example i3e corresponds to 3 and i-3e corresponds to -3. Integers have no size limitation. i-0e is invalid. All encodings with a leading zero, such as i03e, are invalid, other than i0e, which of course corresponds to 0.
+Lists are encoded as an ‘l’ followed by their elements (also bencoded) followed by an ‘e’. For example l4:spam4:eggse corresponds to [‘spam’, ‘eggs’].
+Dictionaries are encoded as a ‘d’ followed by a list of alternating keys and their corresponding values followed by an ‘e’. For example, d3:cow3:moo4:spam4:eggse corresponds to {‘cow’: ‘moo’, ‘spam’: ‘eggs’} and d4:spaml1:a1:bee corresponds to {‘spam’: [‘a’, ‘b’]}. Keys must be strings and appear in sorted order (sorted as raw strings, not alphanumerics).
+
翻译为eBNF语法呢,就是如下
+string : num ':' {CHAR}* |
根据BNF实现的解码代码如下, 把get_content()
方法中path替换为种子文件的路径,运行就可以看到。
+返回的解析结果中会有info_hash
,该值是根据info的bencoding的二进制串计算的sha1值。这个值很重要
+因为之后很多协议都会用到。
# -*- coding: utf-8 -*- |
用最近的毒液电影的种子进行解析,打印如下, +其中announce-list就是tracker列表。
+/usr/local/bin/python3.7 /Users/ym/charm/pytest/ym/bt/parse_torrent.py |
UTF-8
。
在Kubernetes中,Ingress是一个对象,该对象允许从Kubernetes集群外部访问Kubernetes服务。 您可以 -通过创建一组规则来配置访问权限,这些规则定义了哪些入站连接可以访问哪些服务。
- - -这里有篇文章很好的说明了Ingress,并给出了例子 —— Kubernetes Ingress with Nginx Example
-但是要想跑一下,首先要有一个k8s集群。下面首先在mac上安装一个k8s集群。
-我的mbp的配置是8G内存。
-Docker.dmg
。https://github.com/gotok8s/k8s-docker-desktop-for-mac
-使用的方法就是如下,我用的docker desktop的kubernates的版本是1.19.3所以,相应的要把文件images
中的版本号进行修改,以匹配。$ git clone https://github.com/gotok8s/k8s-docker-desktop-for-mac.git |
Enable Kubernetes
和Show system containers (advanced)
,启动,然后耐心等待。需要等待的时间比较长,因为还会下载多个镜像,如docker/desktop-kubernetes
等等。
我们从ingress-nginx的官网可以看到不同的安装方式,对于 Docker for Mac,一行命令搞定
-kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud/deploy.yaml |
这个需要下载镜像quay.io/kubernetes-ingress-controller/nginx-ingress-controller
,所以也需要时间。直到看到如下pods
ingress-nginx ingress-nginx-admission-create-frllm 0/1 Completed 0 56m |
Kubernetes Ingress with Nginx Example中的yaml文件需要外网才能看到。
-我在此列出三个yaml文件
-apple.yaml:
-kind: Pod |
banana.yaml :
-kind: Pod |
ingress.yaml:
-apiVersion: extensions/v1beta1 |
执行命令,等待一下。因为要下载一个镜像“jettech/kube-webhook-certgen”
-$ kubectl apply -f apple.yaml |
查看ingress
-$ kubectl describe ingress -n default example-ingress |
最后测试
-
|
/home/keyvalue/ym/operation
,local path为/Use
remote
在Kubernetes中,Ingress是一个对象,该对象允许从Kubernetes集群外部访问Kubernetes服务。 您可以 +通过创建一组规则来配置访问权限,这些规则定义了哪些入站连接可以访问哪些服务。
+ + +这里有篇文章很好的说明了Ingress,并给出了例子 —— Kubernetes Ingress with Nginx Example
+但是要想跑一下,首先要有一个k8s集群。下面首先在mac上安装一个k8s集群。
+我的mbp的配置是8G内存。
+Docker.dmg
。https://github.com/gotok8s/k8s-docker-desktop-for-mac
+使用的方法就是如下,我用的docker desktop的kubernates的版本是1.19.3所以,相应的要把文件images
中的版本号进行修改,以匹配。$ git clone https://github.com/gotok8s/k8s-docker-desktop-for-mac.git |
Enable Kubernetes
和Show system containers (advanced)
,启动,然后耐心等待。需要等待的时间比较长,因为还会下载多个镜像,如docker/desktop-kubernetes
等等。
我们从ingress-nginx的官网可以看到不同的安装方式,对于 Docker for Mac,一行命令搞定
+kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud/deploy.yaml |
这个需要下载镜像quay.io/kubernetes-ingress-controller/nginx-ingress-controller
,所以也需要时间。直到看到如下pods
ingress-nginx ingress-nginx-admission-create-frllm 0/1 Completed 0 56m |
Kubernetes Ingress with Nginx Example中的yaml文件需要外网才能看到。
+我在此列出三个yaml文件
+apple.yaml:
+kind: Pod |
banana.yaml :
+kind: Pod |
ingress.yaml:
+apiVersion: extensions/v1beta1 |
执行命令,等待一下。因为要下载一个镜像“jettech/kube-webhook-certgen”
+$ kubectl apply -f apple.yaml |
查看ingress
+$ kubectl describe ingress -n default example-ingress |
最后测试
+
|
ChatGPT是基于OpenAI
项目的聊天机器人。
OpenAI
项目由特斯拉创建,而ChatGPT
的母公司当前估值$29bln,据说微软准备购买$10bln。
关联类型是Rust为了解决类型参数(type parameter)之间的依赖关系而引入的。清楚的解释引入动机的文章就是 +RFC095 +。
-目前ChatGPT不对中国开放,想要注册不光需要VPN,还需要手机短信验证。不过注册完成后登录不需要短信验证了。
-具体怎么注册登录ChatGPT可以参考其它文章。
-八皇后问题(英文:Eight queens),是由国际象棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。 -问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击, -即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 -高斯认为有76种方案。
- -直接问ChatGPT, “How to write eight queens algorithm in rust?” -如下图所示,直接就把rust代码实现显示出来,并进行了说明。
- -把rust代码copy出来,增加测试代码test_eight_queen()
。
fn eight_queens() { |
同时,这篇文章详细说明了如何引用关联类型。
+我们在使用Iced(一个跨平台的GUI库)开发一个自定义样式的button的时候, +遇到了关联类型。
+以下代码创建一个button对象,宽度80,高50,样式怎么输入呢?
+button( |
看看这个style方法的签名。
+iced_native::widget::button |
style方法是Button结构体的一个关联方法。
+Button结构体是一个组件,它有两个类型参数,Message和Renderer。
+其中Renderer
类型参数的Bound在where中明确
crate::Renderer
trait.crate::Renderer
trait的Theme
关联类型需要实现 StyleSheet
trait.crate::Renderer
trait如下,有一个Theme
关联类型
/// A component that can be used by widgets to draw themselves on a screen. |
直接运行,没有错误。
-hello $ cargo test --bin hello eight_queens::tests::test_eight_queen -- --show-output |
还是回到这个style方法pub fn style(mut self, style: <Renderer::Theme as StyleSheet>::Style) -> Self
。
这个方法的入参style的类型是<Renderer::Theme as StyleSheet>::Style
,这个PATH的前缀是
+<Renderer::Theme as StyleSheet>
表示实现了Renderer trait的类型的Theme
关联类型
+需要实现StyleSheet
trait。
因此<Renderer::Theme as StyleSheet>::Style
完整的含义就是
crate::Renderer
trait. button::StyleSheet
。<Renderer::Theme as StyleSheet>::Style
就是 SomeThemeType::Style = < ?? >.因此style方法的入参的实际类型就是SomeStyleSheet::Style的实际类型。所以我们要找到SomeRenderer和SomeThemeType。
+crate::Renderer
trait的实现类型有那些,可以看到Renderer<B, T>
impl<B, T> iced_native::Renderer for Renderer<B, T> |
这个结构体如下,
+/// A backend-agnostic renderer that supports all the built-in widgets. |
这个似乎就是我们要找的 SomeRenderer。但是这个Renderer<B, T>
有两个类型参数,第二个就是Theme类型参数。还是没有看到
+这个类型参数具体的类型是什么。继续看哪里使用了这个带有类型参数的结构体Renderer<B, T>
。
/// A [`wgpu`] graphics renderer for [`iced`]. |
这里看到声明了一个新的类型iced_wgpu::Renderer
,并且指定了Theme = iced_style::theme::Theme
。
+这个新的类型在哪里使用了呢?
impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> { |
因此这个iced_wgpu::Renderer
就是我们要找的SomeRenderer,而SomeRenderer::Theme就是
+iced_style::theme::Theme
就是我们要找的SomeThemeType。
iced_style::theme::Theme
类型如何实现button::StyleSheet
trait
+
+从这里可以看到 SomeThemeType::Style = iced_style::theme::Button
+因此style方法的入参是iced_style::theme::Button, 是一个枚举类型。/// The style of a button. |
从定义可以看出,如果我们要实现自定义的样式就需要使用Button::Custom(Box::new(??)).
+而button::StyleSheet<Style = Theme>
类型意思是某类型实现了button::StyleSheet
trait,
+并且关联类型Style = iced_style::theme::Theme.
所以我们可以写一个自定义的button样式了。
+struct ButtonStyle; |
style方法的入参就是Button::Custom(Box::new(ButtonStyle{}))
。
最后style方法的使用如下
+button( |
ChatGPT给人非常惊艳的感觉。我考虑以后经常使用它,如果它一直免费的话。
+Iced
使用的Elm模型非常容易使用。我用它实现一个简单的计算器。
+
关联类型是Rust非常重要的特性。如果不深入理解的话,编译的错误信息都看不明白。
]]>关联类型是Rust为了解决类型参数(type parameter)之间的依赖关系而引入的。清楚的解释引入动机的文章就是 -RFC095 -。
+ChatGPT是基于OpenAI
项目的聊天机器人。
OpenAI
项目由特斯拉创建,而ChatGPT
的母公司当前估值$29bln,据说微软准备购买$10bln。
同时,这篇文章详细说明了如何引用关联类型。
-我们在使用Iced(一个跨平台的GUI库)开发一个自定义样式的button的时候, -遇到了关联类型。
-以下代码创建一个button对象,宽度80,高50,样式怎么输入呢?
-button( |
看看这个style方法的签名。
-iced_native::widget::button |
style方法是Button结构体的一个关联方法。
-Button结构体是一个组件,它有两个类型参数,Message和Renderer。
-其中Renderer
类型参数的Bound在where中明确
crate::Renderer
trait.crate::Renderer
trait的Theme
关联类型需要实现 StyleSheet
trait.crate::Renderer
trait如下,有一个Theme
关联类型
/// A component that can be used by widgets to draw themselves on a screen. |
目前ChatGPT不对中国开放,想要注册不光需要VPN,还需要手机短信验证。不过注册完成后登录不需要短信验证了。
+具体怎么注册登录ChatGPT可以参考其它文章。
+八皇后问题(英文:Eight queens),是由国际象棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。 +问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击, +即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 +高斯认为有76种方案。
+ +直接问ChatGPT, “How to write eight queens algorithm in rust?” +如下图所示,直接就把rust代码实现显示出来,并进行了说明。
+ +把rust代码copy出来,增加测试代码test_eight_queen()
。
fn eight_queens() { |
还是回到这个style方法pub fn style(mut self, style: <Renderer::Theme as StyleSheet>::Style) -> Self
。
这个方法的入参style的类型是<Renderer::Theme as StyleSheet>::Style
,这个PATH的前缀是
-<Renderer::Theme as StyleSheet>
表示实现了Renderer trait的类型的Theme
关联类型
-需要实现StyleSheet
trait。
因此<Renderer::Theme as StyleSheet>::Style
完整的含义就是
crate::Renderer
trait. button::StyleSheet
。<Renderer::Theme as StyleSheet>::Style
就是 SomeThemeType::Style = < ?? >.因此style方法的入参的实际类型就是SomeStyleSheet::Style的实际类型。所以我们要找到SomeRenderer和SomeThemeType。
-crate::Renderer
trait的实现类型有那些,可以看到Renderer<B, T>
impl<B, T> iced_native::Renderer for Renderer<B, T> |
这个结构体如下,
-/// A backend-agnostic renderer that supports all the built-in widgets. |
这个似乎就是我们要找的 SomeRenderer。但是这个Renderer<B, T>
有两个类型参数,第二个就是Theme类型参数。还是没有看到
-这个类型参数具体的类型是什么。继续看哪里使用了这个带有类型参数的结构体Renderer<B, T>
。
/// A [`wgpu`] graphics renderer for [`iced`]. |
这里看到声明了一个新的类型iced_wgpu::Renderer
,并且指定了Theme = iced_style::theme::Theme
。
-这个新的类型在哪里使用了呢?
impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> { |
因此这个iced_wgpu::Renderer
就是我们要找的SomeRenderer,而SomeRenderer::Theme就是
-iced_style::theme::Theme
就是我们要找的SomeThemeType。
iced_style::theme::Theme
类型如何实现button::StyleSheet
trait
-
-从这里可以看到 SomeThemeType::Style = iced_style::theme::Button
-因此style方法的入参是iced_style::theme::Button, 是一个枚举类型。/// The style of a button. |
从定义可以看出,如果我们要实现自定义的样式就需要使用Button::Custom(Box::new(??)).
-而button::StyleSheet<Style = Theme>
类型意思是某类型实现了button::StyleSheet
trait,
-并且关联类型Style = iced_style::theme::Theme.
所以我们可以写一个自定义的button样式了。
-struct ButtonStyle; |
style方法的入参就是Button::Custom(Box::new(ButtonStyle{}))
。
最后style方法的使用如下
-button( |
直接运行,没有错误。
+hello $ cargo test --bin hello eight_queens::tests::test_eight_queen -- --show-output |
Iced
使用的Elm模型非常容易使用。我用它实现一个简单的计算器。
-
关联类型是Rust非常重要的特性。如果不深入理解的话,编译的错误信息都看不明白。
+ChatGPT给人非常惊艳的感觉。我考虑以后经常使用它,如果它一直免费的话。
]]>自从ChatGPT出现以来,就一直在使用,那么ChatGPT毕竟是有局限性的,因为ChatGPT训练的语料是有限的。很多问题回答不了, -也经常会胡言乱语闹笑话。
-但是ChatGPT背后的大语言模型LLM是可以扩展的,也就是说,可以把特定的领域知识让LLM(大语言模型)学习。这样就在一定 -程度上解决了局限性。
-而LangChain项目就是这样的杀手锏,这里是官方文档。
- - -本文代码和例子参考了使用langchain打造自己的大型语言模型(LLMs),对中文资料进行处理。
-LangChain是一个框架,如果要使用,则需要调用大语言模型的API。正好有一朋友申请了OPENAI_API_KEY
,这样就可以开始
-跑跑代码了。
以下是Python代码。
-import os |
Python3.11安装依赖
-pip install -r requirements.txt |
requirements.txt内容如下:
-aiohttp==3.8.4 |
而文本则放在 “./data/“ 目录下,
-天龙八部.txt
内容如下,就是一些明确的信息。
段誉和乔峰,虚竹是兄弟。 |
如果直接问ChatGPT,”段誉的兄弟是谁”, “段誉有几个兄弟”则ChatGPT回答有些混乱。
- -直接运行后,测试提问,如下。
- - -很有意思。
-ChatVectorDBChain.from_llm(OpenAI(temperature=0, model_name="gpt-3.5-turbo"), docsearch, |
自从ChatGPT出现以来,就一直在使用,那么ChatGPT毕竟是有局限性的,因为ChatGPT训练的语料是有限的。很多问题回答不了, +也经常会胡言乱语闹笑话。
+但是ChatGPT背后的大语言模型LLM是可以扩展的,也就是说,可以把特定的领域知识让LLM(大语言模型)学习。这样就在一定 +程度上解决了局限性。
+而LangChain项目就是这样的杀手锏,这里是官方文档。
+ + +本文代码和例子参考了使用langchain打造自己的大型语言模型(LLMs),对中文资料进行处理。
+LangChain是一个框架,如果要使用,则需要调用大语言模型的API。正好有一朋友申请了OPENAI_API_KEY
,这样就可以开始
+跑跑代码了。
以下是Python代码。
+import os |
Python3.11安装依赖
+pip install -r requirements.txt |
requirements.txt内容如下:
+aiohttp==3.8.4 |
而文本则放在 “./data/“ 目录下,
+天龙八部.txt
内容如下,就是一些明确的信息。
段誉和乔峰,虚竹是兄弟。 |
如果直接问ChatGPT,”段誉的兄弟是谁”, “段誉有几个兄弟”则ChatGPT回答有些混乱。
+ +直接运行后,测试提问,如下。
+ + +很有意思。
+ChatVectorDBChain.from_llm(OpenAI(temperature=0, model_name="gpt-3.5-turbo"), docsearch, |