2020-04
30

字典和递归

By xrspook @ 8:48:32 归类于: 烂日记

还记得在看微软的python入门视频的时候。我第一次接触字典这种东西。我觉得那是相当深奥的一件事,因为我搞不懂,那跟列表有什么区别,有什么牛逼的功能。之所以这样,大概是因为微软的视频里字典的录入他们用的是手动输入,显然我觉得一个一个对应太麻烦了。而且用起来的时候我也没发现有什么特别高的效率。入门终归是入门,你看不到字典的牛逼之处,当然就会对学习这东西没什么兴趣。当我在做Think Python 2习题,而且做得死去活来之后。当我用尽九牛二虎之力才终于用列表的方法以二分法搜索的方式找出某个词,而当我后来学习了字典,轻而易举就能找到某个词,效率差了一大截以后,我才明白到字典的超级牛逼之处。显然对我来说,现在我还不怎么熟悉字典这个东西。因为我只是用到了最基本的功能,还有非常多的东西我还不知道,一些它真正的用法我还没有使用到。比如现在的字典我只是停留在第一层级的程度上,通常的键和键值只是某个字符串。如果键值被换成一堆的列表呢?如果一堆列表里面还有一堆的元组呢?想想都觉得这相当恐怖,如果到达那个境界,我该用什么方法去访问那些东西呢?现在对我来说,那是个未解之谜,因为我还没遇到那种题目。

因为工作的原因,我已经放下python一两天了。这种东西一天不练就会手生,下次再重新开始的时候,大概我已经不记得某些语句该怎么写了,于是又得花一些时间去复习之前学过的东西。

还记得在接触python之前,我已经有听说有字典这种牛逼东西。在C语言里好像有,Excel VBA里好像也有,但是我都从来没有用过。因为我觉得那对我来说是非常遥远的存在。在python的学习过程中,我觉得字典就像吃饭睡觉一样,是基本的核心功能。字符串列表字典和元组就像数学里面的加减乘除。当然我这里列举了关系,并不是一一对应的,而是说明他们都是基本的功能,全部都得熟练运用。

还记得貌似是在学递归的那一章。在总结的时候,我记得那里好像说要我们学会不要在一个点上过分纠结,要有全局思维。其实递归并不需要把一个数值竟放进去,然后不断地按照程序进行不断的套用,应该从大局方面想,完成了这个操作,我应该得到什么答案,得到这个答案以后,我就可以把程序继续下去了。这说得简单,但实际上,有时我真想不到,递归最终我能得到什么答案,所以每次我都只能自己很傻地一遍一遍尝试。有些时候我图快,一下子放进去不是最基础的数字,结果就把我自己搞死了。后来才发现,原来一开始进去的东西就应该用最简单的,那才能最快地得出应该有的答案。直到现在,我还是非常难适应这种思维方式。在我没学习递归之前,我已经见识过了斐波那契数列。当时我是用循环的方法实现,但实际上更直观简单的方法是递归。还记得高中的数学里面也经常要我们把某些东西最终以某些方式表达出来,而当时他们给出的题目真是一些递归的东西。所以可能很早以前我就接触过递归了,但是当时没有学编程,也没有计算机,非常苦逼。

如果在学习的时候,能一边的学习编程一边学数学,大概当年就不会那么痛苦了。

2020-04
27

随机应变

By xrspook @ 9:06:39 归类于: 烂日记

python的习题我已经习惯了他们不给参考答案,又或者是参考答案里有一些超纲的东西。既然这样,如果我可以用我学过的东西得出答案,我会努力地那么干,但如果我实在没办法,就会请教搜索引擎,然后我也会用上一些超纲的函数解决问题。现在我只学到了一些很入门的东西,所以实际上现在很困扰我的问题实际上已经有现成的函数可以秒杀掉。秒杀是很简单的,你知道使用范围,然后把东西丢进去就可以了,但如果全部都这样拼凑,跟直接在Excel的系统函数里玩有什么区别呢。知其然,也要知其所以然。经常让我纠结的东西我会想到一些很特殊的情况,我该怎么把那些特殊情况也处理掉呢?当然我想到的特殊情况可能并不算太特殊,又或者还有很多特殊的东西我没有考虑到。内置的函数里,很多东西都固定了取值范围。比如说针对字符串的函数很多东西,你只能在里面填字符或字符串,你不能把列表、元组或者字典丢进去,所以这就很烦恼了,如果我要处理的不只是我能列举的那些字符呢?比如说我要处理的是32个半角的标点符号,我要把他们替换掉,它们32个是以一个字符串的形式放在一个函数里的,你可以直接的把它们引用出来,但是,如果你要把它们替换掉呢?我遇到的问题是,我需要把它们全部删掉。为了实现这个,我写了个循环,历遍了字符串里面的32个元素。然后把它们逐一替换为空字符串。后来我认识了一个比较高大上的函数,叫translate,而在translate之前,又有一个制定翻译规则的函数maketrans。Python3中,maketrans已经被列为内置函数,不需要再引入模块才能使用。Python3的maketrans有一个相当牛逼的功能,就是在创造翻译规则的时候,我可以引入字典。这是一个非常妙的点子!因为在创造翻译词对的时候,强制规定前者跟后者,必须是等长的,而字典的键与键值一定会成对出现。一开始我用那个函数的时候,被翻译的是32个字符,然后我手动数了32个空格进去。后来我为这32个字符建立了一个字典,然后优雅的把字典丢给了maketrans,最终让translate秒杀完事。

关于分隔出一段话里的每个单词这种事,正常人的思路是筛选出那些0-9以及大小写字母。但是,在一开始的时候,我被暗示要用减法。首先,把整段话都变成小写,然后剔除掉里面的标点符号。最终根据分隔符把单词切开。如果一开始,我就想到用限定字符的话,我会从正则方面考虑,但貌似我的做法跟正则出来的效果有点不一样。因为正则之下,居然星号、逗号和杠都没有去掉。这让我非常惊讶。当我对比我的方法提取出来的词和用正则方法提取出来的词以后,我发现在那个排版有点过分的emma文件里,我的提取效果要比网友的正则好。虽然总的来说两种方法算出来的单词量没插多少个,但实际上但把差异打印出来以后,效果还是差得挺远的。

我还是比较习惯自己先琢磨一下,得出自己的方法,然后再去跟别人比较。

2020-04
25

该知道的我不知道

By xrspook @ 11:35:30 归类于: 烂日记

用两天才终于搞懂一道编程题目,这实在是太过分了。如果有人指点的话,肯定不需要这么长时间。如果在我看到这道题的参考答案之前已经完全明白参考答案里面写的所有东西的语法,我也不需要费这么多时间。对我来说,这个理解的过程就像是在猜谜语。什么样的东西是True,什么东西是False。理论上,这非常的简单,但实际上,当真的问起你的时候,如果还没有人跟你说过有这样的规则,你肯定想不明白,对我这种人来说,搞不明白就直接把那丢给python,要他试给我看会是什么的状况。

以前做条件判断,我都是用一些很明白的东西。用一些大家都知道是True还是False的东西,比如说条件是1大于2,这显然是不成立的,肯定是False,不会在这个条件下进行。但如果条件判断的时候,我传进去的是一个列表呢?列表到底是True,还是False?有东西,比如数字、字符的列表是什么?如果里面只有一对单引号,也就是空字符,那又是什么?还有另外一个情况,列表就只是一对方括号,一个空列表,这又是什么?通常来说,我不会给自己制造这些模棱两可的烦恼,如果是我自己写的条件,我不会这么折磨我自己,大概我会加个明确的判定下去。万一我真的把列表传进去作为条件判断。我会问那个列表是不是空列表,那个列表的长度是不是大于0?只要列表里面的东西,列表的长度就肯定大于0,无论里面是数字、字符,又或者是其他列表,甚至有元组,哪怕列表里面只有一对单引号,空字符串,其实也是有长度的,这样的列表长度为1。但空列表,就只有一对中括号的东西,长度会是0。如果在一对中括号里面又有一堆小号呢?从外面看来,中括号是有元素的,但是从里面的小括号元组看来,元组。这些说起来挺尴尬的事,如果你不知道他们的规则。无论如何都是回答不上来,答案是什么呢?这些答案又非常的明白,非黑则白,没有其他选择。

我不知道为什么在同一个判断上面,参考答案用了好几个表达式,是写脚本的人故意在用这种方式考验我们,还是说他有点随心所欲呢?对优秀程序员来说,通常不会犯这样的错误,或者说这能算是错误,应该是有这样不一致的习惯。养成一致的习惯是非常重要的,比如说注释的习惯。也比如缩进的习惯,在python里,缩进就是4个空格,没有说尽基本上程序就进行不下去了,因为通常你都要写个判断循环函数之类的吧。对我来说,我还没有养成空格的习惯,比如说,有些时候我的运算符和对象之间有没有空格,但有时却又。我完全是凭感觉。有些时候我会把那些东西搞得很开,有些时候我会挤在一起。当然,通常这些都不成问题。

在一道编程题上我之所以耗费那么多时间,就正如我上面所说,是因为在一些我应该知道的东西上面实际上我不知道。于是我得出一个结论,在看这个Think Python 2的时候,估计我得拿着本python的手册,一边学一边翻。显然Think Python 2这本书不会把所有规则都告诉你,因为他们想让你自己去学习,掌握那些他们觉得你铁定要知道的东西。

我不觉得用两天时间去研究透一道题是在浪费时间。

2020-04
21

PK我自己

By xrspook @ 19:04:38 归类于: 扮IT

用了几分钟时间,写了个用字典法查找10万单词的词汇表回文词的脚本。字典法肯定要比列表二分法快,但到底快多少呢?实测大概10倍。相比之下,字典法语言实在简练太多。二分法的函数还得考虑递归和起点终点神马,字典法一个in杀到底。

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
# import time
# def in_bisect(library, first, last, myword): # 二分法搜索,10万数据查询最多只需不到20步
#     if first > last: # 这是一句拯救了我的条件
#         return -1
#     else:
#         mid = (first + last)//2
#         if myword == library[mid]:
#             return mid
#         elif library[mid] > myword:
#             return in_bisect(library, first, mid-1, myword)
#         else:
#             return in_bisect(library, mid+1, last, myword)
# j = 0
# count = 0
# library = []
# fin = open('words.txt')
# for line in fin:
#     word = line.strip()
#     library.append(word)
# library.sort()
# start = time.time()
# for i in range(len(library)-1): # 二分法搜索 
#     j = in_bisect(library, 0, len(library)-1, library[i][::-1])
#     if j > -1 and library[i] < library[j]:
#         print(library[i], library[j])
#         count += 1
# print(count)
# end = time.time()
# print(end - start)
# 397, 1.2810001373291016 # 二分法搜索
 
import time
def set_dict(fin): # 字典法搜索
    d = {}
    for line in fin:
        word = line.strip()
        d[word] = 0
    return d
count = 0
fin = open('words.txt')
start = time.time()
mydict = set_dict(fin)
for word in mydict:
    if word[::-1] in mydict and word < word[::-1]:
        print(word, word[::-1])
        count += 1
print(count)
end = time.time()
print(end - start)
# 397, 0.14300012588500977 # 字典法搜索
2020-04
21

循序渐进

By xrspook @ 9:25:39 归类于: 烂日记

我觉得必定把我搞死的筛选词汇表里的二锁三锁单词,居然不怎么费劲就做出来了。

第一次,我在里面用了一个很傻的循环,其实根本就没有必要。可以不用自己的循环就千万不要在词汇表搜索时多用,但我的第一次尝试并不是毫无意义的,,因为已经能得出结果了,而且结果是对的,虽然非常慢,慢到我无法忍受程序继续运行下去。然后,我把要搜索的东西全部都丢到二分法搜索里面,循环只剩下一个必须做的,因为要把词汇表过一遍。第一次做二词互锁的时候,我在一个循环里套了两个if,然后我又把那两个if做成平行的,其实也就是把两个本来嵌套起来的条件用一个and连接起来,很多时候我们都需要这么干。因为除了要二锁,还要考虑三锁,显然人肉去做这些判断,写一大堆一模一样的东西实在太无聊,所以我又自定义了一个新函数,把需要判断的东西全部都丢掉里面,最终返回True或者False。其实,之前我已经考虑过要直接这么干,但因为前天在做搜索回文词的时候我已经得出了回文词的索引,所以直接用就好。我个人觉得,效果是差不多的。

在搜索互锁单词的时候,输出时我用的是字符串的变体。因为二分法搜索被我丢到了一个多条件复合的判断函数里面,所以返回的东西,只有True与False。如果全部都是True的话,就意味着那些单词辩题应该全部都OK没问题。也正是因为输出的东西已经没有索引返回,所以结果我直接使用单词的变体表示。这样的效果很惊人。之前我套用了两个if,然后我写了一个判断函数把条件都含进去了,这样的改进让脚本的运行时间缩短了一半。如果一开始我没有见过两秒多的那个效果,我肯定不会为一秒多的那个惊叹。这是我一步一步琢磨出来了的。

首先是实现得出结果,然后是对语句进行重构,接下要让这个程序适用于二、三词互锁,所以我做的东西是泛化。一开始我把所有判断都写在主程序里,后来我又把它分出一个函数,也就是封装。无意之中,我在执行着Think Python第四章里说到的开发方法:写小程序、封装、泛化、重构。无论是大程序还是小程序,其实都会经历这些。我会从一些我最熟悉的东西开始实现功能。但是我最熟悉的东西不意味着效率一定会高。循环再循环以后,得出一个词都要好几秒时间,实在让人难以接受,尤其当这个词到下个时中间要相隔几秒甚至是十几秒才有反应的时候。这就逼迫着我一定要改进,不同的语句能实现同样的功能,但是它们之间的效率是非常不一样。

大家都在用着二分法的思路去搜索,但是我的脚本就比参考答案快接近30倍。单词一蹦出来,我就知道我一定会比参考答案快。因为我的程序里,单词是噼里啪啦完全没有停顿就全部出来的,而参考答案的词语出来的时候虽然不会一直有,但总有一些顿卡现象,那相对于我的程序来说,就像是慢镜头。

我从来没有想过自己能做得比参考答案还要好,或许他们要做到的并不是有多快。相比于不是二分法的搜索,二分法已经很快了。他们想做到的,大概是用最简练的语言,用模块化的方法实现功能,效率倒不是他们最看重的,因为做搜索词汇表这种事在列表里完成肯定比不上直接上字典。但是,如果不曾在列表里面死去活来,又怎么会体验到字典的神奇。

无论我的二分法搜索写得多么高效,肯定还是不如字典的。我体验过用词典进行斐波那契常数计算。当要计算第40个的时候,一般的递归和字典递归简直就是天渊之别。

如果不曾经历,不曾被整得很惨,我大概就不能说自己真学会了那个东西。

© 2004 - 2024 我的天 | Theme by xrspook | Power by WordPress