斯普拉遁3打工模式联机网络过程分析

引入 今天打工,又被掉线的队友气到了。正好之前根据Nintendo Clients写了一个Wireshark的P2P协议解析插件,又抓过一些打工的网络数据,大概将打工模式的网络交互做了记录,可以做一些简单的分析。 整个打工的交互过程都是通过任天堂的SSL证书,和任天堂服务器分发的密钥进行加密的。想要拿到、或者修改具体在联机中传输了什么样的内容,必须破解Switch以绕过证书的验证,而这就违反了使用协议,会被封禁。我们在本文中使用的工具和方法,仅仅从公开内容出发,不涉及对Switch本身的破解。 这篇文章也是基于本人就网络流量观察的猜测,与任天堂没有任何关联,也不对其中的正确性做任何保证。读完这篇文章,你只能大概知道为什么你的队友/你会掉线,既不能解决队友/你掉线的问题,也不能让你上传说。 我们就游戏的几个阶段进行描述。 匹配 任天堂在本代采用了和怪物猎人相同的新一代NPLN服务器架构。据说是引入了微服务的概念,在各种效果上都有所提升。但很遗憾,除了一些少见的情形外,这对打工几乎没有任何帮助。 打洞与NAT类型检测 目前的网络基础架构使得设备往往都隐藏在路由器的NAT背后。在进行P2P联机之前,需要进行NAT打洞。 一个关于NAT打洞的比喻是,你告诉路由器,我现在去一个住东川路800号(IP地址)202室(端口号)的客人家里串门,他一会儿也来,帮我留个门。路由器里面的NAT表就是看门人,有的NAT啥也不管,有的NAT只放住东川路800号的人进来,有的NAT不仅要检查是不是住东川路800号,还要看房间号是不是202室才让进来。 A,B,C类型的网络,就对应了这三种看门人。两个人要联机,实际上是一个双向的交互过程。比如说,A要和C互相串门,作为中间人的任天堂只需要告诉C A住在哪一号哪一室,C去A那里串门之后,C家里的看门人就会给A留门。读者可以自己想一想A,B,C网络之间哪些可以互联,哪些不能互联。如果感兴趣的读者,可以进一步阅读NAT 类型。 网络活动 游戏向nncs[1,2]-lp1.n.n.srv.nintendo.net所指向的几个服务器发三次三个内容分别为e,f或g的UDP包。服务器会返回9个包,但收得到几个取决于你的网络。包中记录了: 这是在测试哪种类型的连接 你的IP地址和端口号是什么 例如我是B类网络,只收回了6个包。 向游戏服务器发送你收到的IP地址和端口号,通知你准备参加打工了。 在匹配过程中,服务器会推送你的队友的IP地址和端口号信息。此时取决于你的网络,会使用Pia v11协议,发送目标变量ID、源变量ID、包ID均为0的一个握手包。这一过程也进行NAT打洞。 当收到这样的握手包之后,会回复一个只写入源变量ID,其他均为0的一个握手确认包。源变量ID和目标变量ID为随机变量,可能是匹配系统分配的编号。这个编号在一次匹配(就是排到的一组队友,包含“以相同队伍继续”的游戏)中是不变的,但在不同的匹配中不同。 当收到握手确认包之后,意味着握手方到被握手方建立了一个单向的连接。此后就会开始交换加密的游戏信息,表现为目标变量ID和源变量ID对应握手方和被握手方的变量ID,包ID从一个随机数递增。 如果没有收到握手确认,游戏会通知服务器无法连接到对方,此时可能匹配系统会重新匹配其他玩家,并告知对方无法匹配,不再发出对应的包。循环这一步骤,直到都能够互相连接。 这是一个传输过程的例子: 注意图中标为橙黄色,我与波兰IP的玩家联机的三个包。这就是一个握手,握手确认,开始传输加密信息的流程。黑底的三个ICMP包,则表明我的上级路由告诉我,无法连接到俄罗斯玩家,因此通知服务器,也不再向俄罗斯玩家发包了。 有一种情况,双方有一方无法连接到另一方,但运气好(我猜是你俩排位的契合程度,减去可能的延迟,再减去不能连接的扣分项,还是比重新找一个队友的代价高),任天堂会提供TURN协议的中转服务器。TURN协议原封不动地包装了待发送的Pia协议包,由任天堂的服务器提供中转,转发给对方。 TURN协议对于接收的一端是透明的。具体通讯模式可以参考下图。游戏仅会对一对用户某一个方向建立信道,数据在信道中是单向的。 注意下图中TURN包的的数据部分,以幻数32 ab 98 64开始,表明这是一个Pia协议包,也就是任天堂P2P协议包。 当所有队友均准备好之后,便会显示“打工的时间到了”,这时会与游戏服务器进行通信,开始本局打工。 常见问题 这一步常出现的问题,大多是报错。有两种报错形式:游戏界面内的黑色错误框,和游戏模糊化作为背景,Switch系统的错误框。以下记为游戏框和系统框。 游戏框说发生连接错误。 这一步通常不是我方出现的P2P的网络错误。这种情况我猜测是任天堂服务器推送给你的,比如一个队友和另一个队友之间连不上了,但他们都能连上你,你得重新匹配。 系统框说无法连接到服务器/连接错误。 这种情况通常是与你和任天堂的服务器间的连接相关,比如你的连接不稳定,或者任天堂的服务器欠修了。 游戏框说“连接已中断”,一共四行(即封面图)。 这种情况有可能是上面两种的集合。与上述游戏框的区别是系统框的无法连接错误可能通常发生在收到TCP(即你与服务器的连接)错误包、重复包上,而游戏框通常发生在连接断开上。 系统框说无法与对方的游戏机进行连接。 这种情况通常是偶发的,发生于你与队友的UDP连接故障。比如网络不稳定导致的UDP传输发生了多次错误。如果频繁出现,可能需要注意路由器或者加速器的质量,或许存在错误判断NAT类型的情况。 游戏框说未在规定时间内凑齐人数。 这种情况如果频繁出现,需要先检查你的NAT类型。上文介绍了NAT类型,类型越差排到的人就越少。如果NAT类型很好,但仍然很难匹配到人的话,可能需要注意加速器质量,或者你的网络服务提供商是否存在连接阻断的情形。 如果有任意一个队友与任天堂服务器间的网络连接较差,就会出现卡在“打工的时间到了”的界面。如果该队友此时与任天堂服务器断开连接,可能出现“发生连接错误”的游戏框,也有可能在直升机上蒸发。 游戏过程 网络活动 与服务器通信 在整个打工的过程中,只有少数的动作会与游戏服务器通信。因为我无法得到具体的通信内容,这一步可能包括: 投蛋进框 击杀、生成巨大鲑鱼 自己阵亡、复活队友 玩家间通信 其他的所有内容都是仅通过P2P通信完成的。每对玩家间的通信通过一来一回2个Pia协议的单向“通道”(表现为递增的包编号)进行,丢包不会重传。每秒钟向每个队友发送约15个包(和游戏的Tick Rate差不多),每个包的大小在约100B到约700B不等,结算时包的大小可能达到1KB。 P2P通信中分为普通事件和关键事件。移动、涂地等行为是普通事件,只要收到玩家发来的信息(比如移动),就会在游戏中发生。而成为游泳圈、复活、捡蛋是关键信息,只要整个游戏有一个人在关键事件上没有同步,就都认为关键事件未发生。 我根据游戏的掉线行为和向不同人发送的包的大小差别猜测,整局游戏中会按一定规则选取玩家作为同步基准(接下来称为房主),网络较好的玩家可能更容易被选为房主。 图中标为橙黄色的为一个通道,可以注意到包编号是递增的。另外也可以观察到,与一个人交换的数据普遍比其他玩家大,可能是负责同步的房主。图中粉色的条目则为与游戏服务器的通信。 掉线情况的处理有多种。 上述的通道总共有12条。如果某一个通道无法工作,游戏会试图挽救,向NPLN服务器请求建立一个中继(当然也可能直接少人)。需要注意的是,中继是单通道的。比如我收不到一个队友的包,我只会请求服务器中转队友到我方向上的包,同时NPLN服务器会通知这个队友,向我发送的包通过任天堂进行中转。 如果非房主玩家与任天堂服务器断开了连接,该玩家会显示“发生连接错误”的游戏内错误框,游戏服务器会通知所有玩家该玩家掉线了。 如果房主无法连接到某玩家,可能出现即使与任天堂的连接没有断开,也会导致该玩家被游戏服务器踢掉。 如果房主掉线,个人认为可能任天堂没有写重新选房主的代码,因此会结束所有人的游戏,对所有人弹出“发生连接错误”的游戏内错误框。 常见问题 蛋丢不进框:任天堂的服务器欠修了。这一点最近很少见。 队友傻站着,死了不复活:可能是你与该队友的连接出现了问题,但系统仍然在尝试通过TURN转发机制重连。通常运气不好的话,不超过10秒这个队友(或者你)就会掉线。 蛋捡不起来、蛋被队友抢走、队友见死不救:除了第三个可能跟队友的水平有关,产生的原因最有可能是你与房主玩家的延迟比较高。如果某局游戏一开始捡蛋顺畅,但突然开始捡蛋变卡,可能是房主的连接(或者你的连接)出现了重大变动,比如中间链路发生换路,你得准备好掉线了。 结算的时候发生连接错误:结算是由房主上传到任天堂服务器,再由任天堂服务器下载到你的设备上的。当房主上传完毕后,结算就已经记录了。因此如果查看历史,没有正确结束,说明房主掉线;如果有正常的结算,说明你与任天堂的服务器连接不稳定。

2022/11/9
articleCard.readMore

DHCP无类型静态路由──一个摆设

引子 过年回到老家,连着2.4GHz Wi-Fi,在电视里过年的歌曲和相声,与长辈们的麻将声中,和争分夺秒在14天里打完一个寒假的游戏的小孩们抢总共不到100M的网速;不止如此,爱看的网站个个都是CONNECTION_RESET,DNS_PROBE_FINISHED_NXDOMAIN,ERR_NAME_NOT_RESOLVED,着实感觉生活不易。我翻出我好久之前买的树莓派,打算搭一个单臂路由,来解决众多设备的上网难题,作为软路由的替代。 但是单臂路由就有一个问题。只要把网关指向单臂路由而不是主路由,数据就要在单臂路由的臂上往返。之前买的4B版网络还算行,但找不到了,3B版的有线网速度就比较让人着急了。相当于总的带宽被树莓派的收发速率所限制,而且单臂的做法,也使得原本的全双工互不干扰的传输变成实质上的半双工传输,上传和下载的带宽共同占据同一个带宽。 但是办法总比问题多,配一个路由表,有些网站直接从主路由出去,有些网站走树莓派,分流了效果不就好了吗?主路由想想也是NetGe*r, Mercu*y, T*-link这种主流厂家的产品,现在都是买路由器当摆件的年代了,支持路由表想必不太现实。而用户端的路由表,看着我手上这些设备,能手动配置路由表的都寥寥无几。 不让用户配,总得让网络有一个配置方法吧。毕竟路由表是三层网络的核心部件,总不能靠设备自己猜网关在哪儿啊。DHCP提供了Router选项,用来配置该网络的网关。而同样由DHCP提供的121 Classless-Static-Route,RFC3442 [1]写明了是用于配置无类型网络(CIDR)静态路由的。打开Wireshark抓个包,看到本机发出的DHCP request包里面带了这个选项,内心一片窃喜。如果能够把路由表通过DHCP配置到每个设备中,就可以显著降低树莓派的流量了。 选项121:无类型静态路由 DHCP协议想必读者们都不陌生,但通常也就止步于通过DHCP用于获取本机的IP地址、子网掩码和网关信息,用于配置IPv4协议栈,也就是第一个广播Discover包和返回的Offer包。实际上,客户端会接收到相关配置信息的Offer时,会再次广播一个DHCP Request包用于向整个网络确认。这一IP如果被确认,就会返回一个ACK包。 实际上,除了获取常见的网络参数,DHCP上还承载了更多的功能。DHCP通过选项,对这些功能进行分类。除了IP地址、子网掩码等之外,还有一些不常见的功能,例如指定DNS服务器、LDAP服务器、POP3和日志服务器,还提供了TFTP选项和用于PXE网络启动的各种参数等。 参数的数量非常多,DHCP采用请求机制。即客户端将需要获取的选项字段记录在request包中,DHCP服务器对应这一请求返回有效的字段的内容。 Linux上常用的DHCP服务端在发出DHCP request时,会请求编号为121和249的选项,这些选项的目的是配置无类型静态路由。编号121是RFC3442指定的,而249则被早期微软的DHCP服务器所使用。因为我没有Windows的设备,我们侧重在121选项上,249选项的配置方法应该是类似的。 RFC3442指定,如果121选项有效,则以其中的内容覆盖网关配置和已被废弃的选项33:有状态静态路由配置。选项是以String,或者说uint8数组存储的。每一个条目按照一下方式排布: 1 子网前缀长度,网络地址的显著位,网关地址 即,例如12.34.56.0/23的网关在192.168.1.1,192.0.0.0/7的网关在192.168.1.2,而0.0.0.0/0(默认网关)在192.168.1.1,则需要写为: 1 2 3 23, 12,34,56, 192,168,1,1, 7, 192, 192,168,1,2, 0, 192,168,1,1 逗号、空格和换行是用于分隔8位无符号整数的,不会加入数组。上面的部分总共有19个数,在包中会以 1 2 3 ----------------------------------------- | 121 | 19 | 23 | 12 | 34 | 56 | 192 | .... ----------------------------------------- 的形式存储。前两位的121是选项编号,19是数组长度。 问题:溢出 如您所见,数组长度也是用一个8位无符号整数来存储的,这意味着最多只能够表示255个字符。我们看到,一个前缀长度为24的条目就需要占用8个字符,255个字符能够表述的条目数量可能大概在30-40条左右。相比于分配给中国的网络数量(APNIC大概在8000条左右,做了进一步的聚合之后大概在5000条左右),简直是杯水车薪。 同样考虑到条目大小的问题,在RFC3396[2]中,DHCP协议引入了编码长可变长度数组选项的连接(Concatenation-requiring)。简单来说,DHCP的条目线性连接,以255选项结束。DHCP每一个条目的长度不能超过255个字节,当出现多个同一选项的条目时,支持连接选项的客户端需要将这些条目的内容按照出现的先后顺序连接。例如客户端收到如下的选项: 1 2 3 Option MagicNum [length 2] 192,168; ... Option MagicNum [length 3] 1,0,24; 将会按照顺序连接起来,并记录为MagicNum [length 5] 192,168,1,0,24。 为了兼容性,该RFC还建议DHCP服务器确定客户端是否支持分段。例如,一旦DHCP客户端请求了需要分段的选项(如121选项),那么服务器就应该假定该客户端支持分段,并严格按照分段来发送,否则就不发送需要分段的选项。 我们观察到,Linux上的常见DHCP客户端与网络配置工具,均支持这一分段选项的接收(否则就不能请求需要分段的选项了)。但目前常见的Linux DHCP服务器,均不支持分段发送。尤其令人大跌眼镜的是ISC的dhcp,其配套的客户端支持这一分段选项,而服务器并非如此。例如一个长300字节的路由表,服务器会将长度标记为255(最大值),但将整个长为300字节的路由表接在后面,发生了选项越界溢出。多出来的这45个字节会被DHCP客户端解释为其他的选项。如果字节无意义还好,在解包的时候会认为发生错误并丢弃;如果恰好能够解释为特定的内容,就会导致非常奇怪的结果。 DHCP包的大小问题 就算通过一番奇技淫巧,把分段整出来了,还会面临另一个难关:DHCP包太小,路由表塞不下。 可是DHCP包设计之初就考虑到了大小问题,其长度字段是16位整数,即65536个字节。装这么个路由表绰绰有余。这其中的缘由,就又牵扯到实现问题上了。 DHCP包虽然是以UDP协议包裹的,但和UDP、TCP等常见协议不一样。因为其工作在非常特殊的阶段,此时客户端可能还没有建立一个完整的IPv4协议栈,最早发出的DHCP Discover/Offer包为了稳定不会分段,以防止一些客户端不能正确处理。在这里MTU就限制了DHCP包的最大大小。而一些程序为了省事,将这个大小进一步缩小为DHCP协议的最小长度576字节。 如果你仔细观察最初的DHCP Discover包的内容,就会发现,其请求的选项都是基础而必要的选项。而更长、可能超过MTU限制的选项则会放在第二次的Request中。此时的Request,按照规范,可以附有客户端能够接受的最大包长度,以供服务器按需排布恰当的内容。 但是有哪些客户端这样做了呢?几乎没有。为了对付这些无信息的客户端,DHCP服务器会设置一个可以接受的大小,以此为发送的上限。有些DHCP服务器会设置为与MTU相关的大小,而有些DHCP服务器则会按照576字节(最小值)来配置。ISC的DHCP服务器实现甚至限制最大值为1500,就算MTU设置得再大,客户端可以接受的包再大,也不会发出超过1500字节的包(甚至IP包都配置为Don’t Fragment)。需要注意的是,IP协议是有分段(fragmentation)的,除了Offer包的兼容性问题之外,其他包的大小并不会受到单个发包MTU的限制。 无用之物 DHCP能出这么多问题,也是因为DHCP客户端的实现过于奔放,跨越二十年,支持的字段各有不同,为了兼容性只能如此。 Android和iOS的客户端都不支持这一选项,这些设备的DHCP实现压根就不会请求121选项,即使收到这些选项也会忽略。这就使得这个功能比较鸡肋了。 虽然DHCP协议有足足255个选项,大部分的选项都分配了固定的语义,但因为DHCP协议客户端和服务端的设计者各自为政,真正使用的选项,也就只有几个而已。同时也因为网络本身的发展,诸如SDN等概念的提出和“越俎代庖”的各种网络设备,DHCP协议在未来网络中扮演的角色也会越来越边缘化。 Ted L., Bernie V., et al. "The Classless Static Route Option for Dynamic Host Configuration Protocol (DHCP) version 4", RFC 3442, December 2002. ↩︎ Ted L., Stuart C. "Encoding Long Options in the Dynamic Host Configuration Protocol (DHCPv4)", RFC 3396, November 2002. ↩︎

2022/1/31
articleCard.readMore

配置透明代理,实现无感上网

一直让我不解的是,我的整个博客只有“Linux配置SSR”这一篇博文,质量也不算高,内容也没什么价值,发了这么长时间,都能获得不错的点击量(貌似是博文里点击量最高的)。那篇文章中的工具electron-ssr,不能够以系统代理的形式接管流量,ssr本身支持的功能也不太好。因此就写了这篇文章,作为进阶读者的参考。 穷则独善其身,达则兼善天下。本文介绍的方法,不仅可以作为单机流量配置的参考,也能作为路由器(软路由)的配置策略。我目前的上网工具就运行在软路由上,供整个局域网使用。 先决条件 不是OpenWRT,并使用Systemd启动 OpenWRT有诸多的插件,可以快速配置透明代理,如OpenClash,fancy-ssr等。同时,下文的配置因为使用的是Systemd启动的Linux(当今流行的发行版中,绝大多数都采用Systemd),不适用于OpenWRT。 有root权限 此教程中的方法接管系统流量,需要配置路由表和防火墙。如果你没有root权限,可以使用引子里提到的文章,配置用户态的代理工具。 Clash的配置 Clash是比较常见的上网工具,其支持、整合多种协议,且与Linux端的网络栈配合较好。程序文件可以在GitHub上下载,Arch Linux也提供了官方的软件包。 需要注意的是,GitHub上提供两种版本,分为开源版本和闭源的Premium版。Arch Linux只提供开源版本的软件包(闭源版本貌似在AUR中提供),下载时应当注意区分。本文以开源版本示意,如果你需要Premium版提供的特性,也可以对应下载。 配置文件 我们将配置文件放置在/etc/clash中。你从你的服务提供商处可以获取到config.yaml配置文件。需要修改的地方有: 1 2 3 4 5 6 7 8 9 allow-lan: true # 局域网的连接 dns: # DNS接管 enable: true enhanced-mode: redir-host listen: 0.0.0.0:53 nameserver: - <your name server> mode: Rule redir-port: 7892 allow-lan: 是否允许局域网连接。 dns: 配置没有污染的DNS。注意:这是必须的[1]。 mode: 按规则匹配。 redir-port: 路由表转发的端口。这和内建http代理、socks5代理端口都不一样。 Clash与Systemd集成 基础服务与开机启动 在/etc/systemd/system/文件夹下新建clash.service文件,并写入以下内容: 1 2 3 4 5 6 7 8 9 10 11 [Unit] Description=A rule based proxy in Go. After=network-online.target [Service] Type=simple Restart=on-abort ExecStart=/usr/bin/clash -d /etc/clash [Install] WantedBy=multi-user.target 使用sudo systemctl enable clash命令来启用该服务。 自动订阅更新 你需要准备一个订阅更新的脚本,比如用wget下载,用sed调整配置后覆盖原本的/etc/clash/config.yaml文件。 在/etc/systemd/system/文件夹下新建clash-subscription.service文件,并写入以下内容: 1 2 3 4 5 6 7 [Unit] Description=Clash subscription update After=clash.service [Service] Type=oneshot ExecStart=/usr/bin/bash "你的脚本文件" 在/etc/systemd/system/文件夹下新建clash-subscription.timer文件,并写入以下内容: 1 2 3 4 5 6 7 8 9 10 [Unit] Description=Clash subscription update [Timer] OnCalendar=<your timer> RandomizedDelaySec=5m RemainAfterElapse=no [Install] WantedBy=timers.target Timer的设定可以参考Arch Wiki-Systemd-Timers。 最后启用该定时器(注意不是启用服务)。 检测到配置文件改动后重启服务 在/etc/systemd/system/文件夹下新建clash-watcher.service文件,并写入以下内容: 1 2 3 4 5 6 7 8 9 10 11 12 [Unit] Description=clash watcher After=network.target StartLimitIntervalSec=10 StartLimitBurst=5 [Service] Type=oneshot ExecStart=/usr/bin/systemctl restart clash.service [Install] WantedBy=multi-user.target 在/etc/systemd/system/文件夹下新建clash-watcher.path文件,并写入以下内容: 1 2 3 4 5 6 [Path] Unit=clash-watcher.service PathChanged=/etc/clash [Install] WantedBy=multi-user.target 启用该systemd path(注意不是启用服务)。 配置系统转发 要实现无感上网,需要通过内核的网络包netfilter将特定规则的包转发到上面配置的redir-port。如果你不希望这样做,也可以在上面的配置文件中直接添加HTTP proxy的端口,将需要的程序流量进行转发。 netfilter有多种工具提供控制与操作。如果你没有特殊的控制需求,建议使用firewalld而不是iptables或nftables。firewalld可以提供一个更加抽象、便于操作的界面,也能够为网络,尤其是作为网关的网络提供一个更强的、开箱即用的防火墙。 本文提供两种方法,基于nftables的和firewalld的。 基于nftables的转发 在您的/etc/nftables.conf中添加如下项: 1 2 3 4 5 6 7 8 9 table ip filter { chain proxy { ip protocol tcp redirect to :7892 } chain output { type filter hook output priority 0; policy accept; goto proxy } } 上面的内容只是示例。请按照以下的需求对table和chain进行调整: 本机:ip filter表中的output 作为路由转发:ip nat表中的prerouting 上述条件不是互斥的,如果你同时需要,则需要同时配置。如果你的设备上还有以虚拟网络形式构成的虚拟机、容器,则也需要配置路由转发。每一张不同的table中都需要配置proxy链。 基于firewalld的转发 在/etc/firewalld/direct.xml中添加下面的rule: 1 2 3 <direct> <rule ipv="ipv4" table="nat" chain="PREROUTING" priority="-20">-p tcp -j REDIRECT --to-ports 7892</rule> </direct> 具体的table和chain调整如上一章节所示。 本地/特定地址跳过转发 如果你不希望一些特定地址(如私有地址)也通过clash,需要配置跳过的规则。clash本身的规则已经提供了一个可以调节的界面,但netfilter是内核组件,转发效率比clash高。 nftables的配置方法: 先定义私有地址: 1 2 3 4 define private_route = { 172.16.0.0/12, 192.168.0.0/16 } 在proxy链中,redirect语句前加入以下语句: 1 ip daddr $private_route return firewalld的配置方法: 在上述规则前配置以下跳过规则: 1 <rule ipv="ipv4" table="nat" chain="PREROUTING" priority="-20">-m set --match-set <your private route> dst -j RETURN</rule> 同时需要在/etc/firewalld/ipsets/<your private route>.xml中建立你所需要的条目: 1 2 3 4 <ipset type="hash:net"> <entry>192.168.0.0/16</entry> <entry>172.16.0.0/24</entry> </ipset> 这里有根据国家分配的IP地址列表,可以供配置时参考。 DNS服务器的配置 你可以使用clash自身提供的DNS服务器污染检测机制,也可以选用DNS-over-TLS/DNSCrypt/DNS-over-HTTPS来作为无污染的DNS源。笔者使用的是dnscrypt-proxy这一程序。鉴于篇幅此处就不详细介绍。 Clash域名匹配的原理,是在客户端DNS查询时构建域名-IP的对应表,并以此将请求的IP与域名联系起来,作为域名匹配的目标域名。如果关闭Clash内建的DNS,基于域名的匹配规则就是无效的。enhanced-mode中还有其他的选项(fake-ip),可以确保这一匹配的正确性,但因为影响用户获取的IP地址,且需要配置其他的选项,此文不使用该方法,感兴趣的读者可以进一步探究。 ↩︎

2021/11/6
articleCard.readMore

Generating Unlimited Grammar: A Messenger Perspective

Chomsky hierarchy describes a containment hierarchy of classes of formal grammars. Lower level of languages, like regular and context-free, are well studied, and used in multiple practical fields, like regular expressions, and programming languages parsing. Most textbooks introduce how to construct deterministic finite automaton from regular expressions, and some textbooks focusing on compilers will present LL, LR, LALR, etc. to show how a subset of context-free grammars are transferred to parsers. These topic are well researched, and if you use bison, it will even show you how reduce-reduce, shift-reduce conflicts are happening by examples. When the problem comes to higher classes in the Chomsky hierarchy, the problem becomes difficult. C++ has a context-sensitive grammar, and GCC have to use a hand-written parser for it. Most textbooks will stop at context-free grammar, and teach you to use the pumping theorem to prove that some languages are not context-free. For these languages, it’s even difficult to describe them with a correct grammar! Take $a^n b^n c^n, n \ge 0$ as an example (A popular and easy non-context-free language, commonly used to demonstrate pumping lemma). There are no definitive algorithm to guarantee a grammar for it. Sentinel and Combination A common solution for this can be sentinel and combination. In our example, we can first combine the last two part as $a^n (BC)^n$, which can be generated as context-free grammar, and with the sentinel $CB \rightarrow BC$, arrays like $BCBCBC\cdots$ can be derived as $BBB\cdots CCC$. This can be obscure, if the part increases. We need to either combine recursively, or create larger sentinels to do the part. When the part is not definitive, the problem cannot be solved. Take this problem as another example: Let $\Sigma = \{1\}$ and $L_{\text{square}} := \{1^n \mid n \text{ is a perfect square}\}$. So $\epsilon, 1, 1111, 111111111$ are in $L$ but $11, 11111$ are not. Give an unrestricted grammar generating $L_{\text{square}}$. Square numbers can be expanded to $1 + 2 + \cdots + (n-1) + n + (n-1) + \cdots + 2 + 1$. If we take this route, the problem could be difficult to solve by the combine method, as the items in the sequence increases with $n$. Messenger Perspective Now we show a new perspective to view the problem. Taking the string as a queue, and messengers moving along them between head and tail. When certain situations are met, messengers will change the sequence, otherwise they’ll move through the queue. This method will have longer reductions, but it will be easier and more intuitive to construct, and having less rules. In the second problem, we separate the 1’s with 0, to specify a border for messengers to recognize. And we add symbols [ and ] to indicate the head and tail. A state in the reduction could be [01011010]. We can have 2 messengers to solve the problem. messenger A, from head to tail: insert new groups, i.e. 010, at the head and tail. increase inner groups’ 1 number messenger B, from head to tail: remove all zeros, head and tail symbol, to reformat the string into the final look. With the border of each group, the recognition field, i.e. the range it looks to derive uniquely, decides the size of its rule set. We here define these messengers with grammar: $$\begin{align*}[ &\rightarrow [01A \\A0 &\rightarrow 01A \\A1 &\rightarrow 1A \\A] &\rightarrow 0] \\\end{align*}$$ and $B$: $$\begin{align*}[ &\rightarrow B \\B0 &\rightarrow B \\B1 &\rightarrow 1B \\B] &\rightarrow \epsilon \\\end{align*}$$ and the start symbol: $S \rightarrow []$. Only 5 variables and 9 rules is needed. Exercise Give the unlimited grammar generating language in the first problem. Hint: You may need to deal with situations when two messengers meet, causing a deadlock. (e.g. Emulate a lock by changing the head symbol to allow at most one messenger traveling in the sequence). Let $\Sigma = \{1\}$ and language $L := \{1^k \mid k \text{ is prime}\}$. Give an unlimited grammar generating $L$. Hint: Here is a not-so-clever path doing it: list all numbers by increasing order use Eratosthenes Sieve to filter primes select one and remove others Not only head symbols can send messengers, and the direction are not limited from head to tail. You may change the head and tail symbol to indicate stages. You may need to deal with situations when messengers in different directions meet, causing a deadlock. (e.g. by explicitly swap them with $AB \rightarrow BA$).

2021/11/5
articleCard.readMore

LaTeX插入其他PDF中的矢量图

引子:矢量图 $\LaTeX$的使用者,常常会遇到插入图表的情形。TikZ是非常常用的图表工具,但是用TikZ绘图的难度可能比较高,有些时候,诸如Matlab,pyplot之类的工具可能用起来会更加方便。 一个解决方法是导出成png。png算是最通用的,也是支持特性最多的图像格式。相比于jpg等格式,其支持透明背景的特性能够更好地在高级排版中运用。 但是png格式有一个问题。其储存的图像是以标量图的形式展现的。这就导致其经不起放大。远看PNG图像没有什么问题,但放大之后就会出现锯齿。 标量图与矢量图 至于什么是标量图,什么是矢量图,这要从图像的存储结构说起。具体的实现会比较复杂,我们就从他们是怎样显示的,简要对比其中的区别。 你的显示屏,其实是由很多个像素点构成的长方形点阵。如果你凑近看,你能看见色彩斑斓的像素点。每个像素点上显示什么,是由你的显示卡来处理生成的。很容易就能够想到,这种点阵上,不同斜率上的点的距离是不同的。比如45度的斜线,他的点之间的距离就是横向和纵向排列的1.4倍。而如果是更普遍的角度,可能整个显示屏上都不存在能排列在同一条直线上的点。这样的绘图方式对于人眼是难以接受的。 因此,我们采用了一些近似的算法,来让线条看上去是那样的。如果你玩过旧主机,或是像素风的游戏,就能够体会到这一点。当代的算法会高级许多,通过插值模拟的方法,这些算法能让你的显示看起来更平滑。 标量图的原理和显示屏相同。他们储存的是点阵。计算机在显示标量图的时候,因为不清楚图像的实际内容是怎么产生的,因此只能以上图左边的形式,把像素放大到你需要的大小。 而矢量图则不同。矢量图存储的是一系列描述如何显示你需要的图像的指令。计算机直接向图形卡发送绘图的指令,因此图形卡能够直接按照你的显示器尺寸绘制响应的图形。比如你计算机上安装的字体,就是一种矢量图。上面的图片右边的abc看起来很好看,但如果你把这张图放大,最后也能看见像左图一样的插值色块。但文字‘abc’,你把网页缩进到再大,换再高清的屏幕,显示效果都是同样地好。因为你无论如何缩进,文字都是由图形卡来绘制的。而图形卡的高级特性,能够使你看见其设计上最清晰的内容。 我把标量图分辨率调高,不就行了吗? 这又是另一个问题。调高图片的分辨率,看上去显示效果很好,但这样的图形缩小后的显示效果不太好。同样,因为不清楚图像的实际内容是怎么产生的,对于更小的像素,也只能够通过平均的方法来展示。这样的展示方式通常情况下问题不大,但仍然会出现模糊、虚化的问题。尤其在字体上。上图的两个文字,你如果走远一点,或把眼睛眯起来看,就会发现他们的形状是有差异的。 就算你经过精心选择,找到了完美的图片分辨率(比如说直接截屏),打印的效果也与显示不同。 就算是很好的显示屏,比如15英寸4K屏,每一个英寸中能排列的像素点也不到300个。而以笔者20年前的激光打印机为例,每个英寸都能打印1200个像素点。这种素质称为DPI/PPI,是由设备的特性决定的。打印机同样是一种显示设备,不过他将内容显示到纸张上。因为显示原理上的不同,打印机能够很高效地提高显示的素质,而显示屏则受制于屏幕与半导体技术。你会发现,打印机打出来的字几乎像是从模子里印出来的一样,就是这个原理。 因此,能够使用矢量图的时候,就尽量不使用标量图,是每一个使用$\LaTeX$,对排版有着高标准的人都应该具备的策略。 很多图表工具,比如Matlab,pyplot,都能够将结果以矢量图的形式输出。目前笔者常用的是eps格式,不需要额外调包,而且调节大小都支持。eps,即封装PostScript。PostScript是一种打印机图形描述文件,但支持这种文件的打印机很少,使其真正广泛应用的则是PDF,PostScript的后继者。 这就出现了一个问题。其他的PDF里面也有我需要的矢量图,如何将其截取出来呢?截屏显然是一个错误答案。 从PDF中导出图形eps 这里需要使用一个工具:Inkscape。Inkscape是绘制矢量图的开源工具,支持多种平台。 安装后新建文档,选择导入 打开需要截取的PDF,找到有需要的图形的那一页,进行导入。如果你的电脑字体安装不全,建议使用从Poppler/Cairo导入。 选取需要的图形后,使用编辑-反选,并删去其他页面上的内容。 选择文件-另存为,文件格式选择eps格式。文字输出选项可以自行调节。 这样需要的图形就被导出为eps格式了。如果你选择了“创建LaTeX文件”选项,还会有一个eps_tex后缀的文件生成。这种方式可以使用LaTeX来处理图形中的字体。 导入LaTeX 前者可以直接用\includegraphics导入。例如: 1 2 3 4 5 6 7 8 9 10 % \usepackage{graphicx} % \usepackage{transparent} % \usepackage{color} \includegraphics{example.eps} \begin{figure} \includegraphics[width=0.6\textwidth]{example.eps} \caption{Example} \label{example} \end{figure} 如果你输出的是eps_tex文件,那么导入的方式需要更改。 1 2 3 4 5 6 7 8 9 10 11 % \usepackage{graphicx} % \usepackage{transparent} % \usepackage{color} \input{example.eps_tex} \begin{figure} \def\svgwidth{0.6\textwidth} \input{example.eps_tex} \caption{Example} \label{example} \end{figure}

2021/10/21
articleCard.readMore

光速不变原理和迈克尔孙──莫雷实验

本文旨在对光速不变原理这一基础理论做出科普性质的阐述。 早在古希腊时期,人们就发现了静电现象,中国古代的文献也有利用天然磁石指明方向的记载。库仑用库仑扭秤和电摆实验解释了静带电体之间的相互作用;安培观察到通电导线使磁针偏转实验,说明了电流产生磁场;法拉第通过电磁感应实验说明了变化的磁场产生电场。这表明,电和磁之间是密不可分的。 光在此时还没有与电和磁相关联。人们当时所能稳定产生的光源几乎都是热致发光和化学发光。在天文实践中人们发现光的传播是有速度的,而在当时,设计精密的旋转齿轮实验装置也使人们初步测定了光速的大概值。麦克斯韦通过对电学和磁学的已知结果的理论分析,在理论的推导下提出了电磁波方程,并此后提出了麦克斯韦方程。而电磁波的存在也被赫兹用实验证实。通过理论计算所得的电磁波的速度与光的速度相近,麦克斯韦猜测光是一种电磁波。光能够如电磁波一样激发一些金属导体上的电子使其逸出,其能量与麦克斯韦的电磁理论基本符合。 至于物理定律的正确与否,我想起中学时期物理老师的一句话:“物理定律自从提出就自动成立,直到实验结果将其推翻为止。”如果既有的物理理论与新的物理定律存在矛盾,那么一定有实验能够证伪其中一个。麦克斯韦的电磁理论在今天仍未被推翻,通过此理论指导下的工程实践在你我的身边都随处可见──电话天线、无线充电、GPS网络等等。事实上,电磁学理论已经是当今最完善的一个物理学科之一。其精确程度甚至允许人们以此来定义米这一国际度量单位。 然而电磁场理论本身却与牛顿第一运动定律所规定的伽利略参考系相矛盾。这是电力与磁力的不同表现形式所造成的。我们以一个简单的例子来说明。对于一个“惯性系”,即满足牛顿运动定律的参考系,中摆放的两颗静止的带异种电荷小球,此时两者之间只存在静止带电体之间的作用力──库仑力。如果不将其固定,两者之间就会互相吸引。而以另一个,以垂直于两点连线的恒定速度移动的坐标系(这也是一个惯性系,是由牛顿第一定律规定的)来看,此时的运动电荷形成了电流,而反向流动的电流之间会有排斥力──洛仑兹力。这些值都是可以精确计算的,而当移动速度到达一定值(光速),两个力之间就会相互抵消。当移动速度超过这个值,合力就会变成排斥力。这显然是不满足牛顿运动定律的。 然而结果我们都知道,库仑电摆(和扭秤)实验就已经告诉我们了。这就指出,如果要坚持牛顿运动定律,修正电磁定律,那么一定存在一个“绝对”参考系,电磁力中关于速度的计算都基于这个绝对参考系。这便引入了以太。力的传播需要介质,电磁力也不例外,其介质便是以太。在绝对参考系上,以太是静止的,这样就可以不需要修正绝大多数的力学定律,只需要对电磁定律进行相应的变换即可。我们上文提出的例子是由洛仑兹设想的。麦克斯韦提出了以太,而洛仑兹本人是以太学说的拥护者,他给出了一套电磁力的变换公式来计算一个运动参考系相对于绝对参考系的电磁力,这样就能够使得电磁的总作用力相同。这个变换公式正是后来狭义相对论的经典推论──洛仑兹变换(尽管形式不同),尽管后来的应用与他设想的并不相同。 然而地球本身在以30公里每秒的速度绕着太阳转,赤道上则是以大约1600公里每小时的速度自转。这个速度的方向时刻在改变,相对于绝对参考系,地球上的速度又快又在变化,以太相对于我们以相当高的速度运动,即迎面吹来的“以太风”。这便引入了迈克尔孙──莫雷实验,这个实验的目的就是测定我们相对于绝对参考系的速度。 迈克尔孙──莫雷实验的设计思路是,分开同一束光,同时在两个垂直方向上走过一段距离再观察,通过波动干涉,我们可以得到两者走过的时间差。再旋转90度,这个时间差会发生变化,会在干涉条纹上展现出来发生了移动。通过不同角度的测量,我们就可以得出我们相对于以太的运动速度。这就类似于伸出手来感受风向,你旋转你的手,风吹的感觉就不一样。 但是这个实验做出来的结果是不存在这股风。也就是说,实验地点相对于以太静止参考系的速度是0。人们在不同海拔,不同纬度,不同的时间做了这个实验,得出如果存在以太,那么以太参考系和地球参考系是一致的。虽然没有以太风刮到地球上,但地球就是以太风的风眼,太阳上的以太风速是一千万米每秒──这已经足够观测到了。 电磁定律修正失败,爱因斯坦开始在牛顿运动定律上做手脚,最后提出了狭义相对论。准确地说,狭义相对论只有两条基本原理: 在所有惯性系中,物理定律具有相同的表达形式。 在所有惯性系中,真空光速具有相同量值,与光源的运动无关。 前者几乎是朴素的,继承了整个经典物理学的内容,而后者就是文题所述的光速不变原理。实际上,科学界对光速的测量也是相当精确的。如果存在一个随地球旋转的以太参考系,那40亿公里外(如果你觉得这个数字很大,这只有0.0004光年,海王星都比这个远)的任何星球在其发的光掠过地球表面的时候,都会像超音速飞机的音爆那样亮(而且没有质量的物体旋转是违背牛顿运动定律的)。若非如此,地球的自转足以在早晚之间提供将近1000m/s的光速测定差值。1958年人类测量光速的精度就已经在100m/s左右,这样的误差是很难逃脱实验人员的法眼的。 当代“以太”这个词语仍然在使用,例如“以太网”。虽然静止以太的观点不再存在了,但电磁的相互作用仍然需要一个介质来传递。另一个物理学分支──量子力学提供了一个令人信服的解释,感兴趣的读者可以自行查阅相关资料。 我长期认为,让高中生和本科生接触基础科学的训练,其目的并不在于能够掌握多少知识,而是培养一个人的科学素养与严谨的态度。但令我吃惊的是,尤其是一些计算机领域中颇有建树的人,包括一位编译器领域的专家,曾经在四川唯二的985高校中完成了本科(据我所知,该高校的物理教育在四川还不错,仅逊于成都七中),而如今不仅对这些高中层次的常识认知都有些偏差,连推理实证的态度都非常匮乏。 阴谋论在如今的科学领域是站不住脚的。发一篇普普通通的光速测量的论文可能连教职都拿不到,但如果能够证明光速是在变化的,就算国际大型阴谋科研集团阻挠你发表,找个三流报纸把事情捅出去,至少也是吃喝不愁。如果在某些国家,至少能够像李森科那样混个院士,实验想做几台就做几台,论文想发几篇就发几篇。也不难测,请两个物理研究生,按照这篇论文来搞,打个飞的去马尔代夫,一天就搞定了,到时候估计都是专机来接,吃香的喝辣的,还卖什么课啊。(注:如果做不出来,本文作者不提供返程票报销事宜)

2021/7/28
articleCard.readMore

交大葡萄演义

本内容是虚构创作,与文中所提到的人物、事件、网站、机构、协议、端口没有任何关系。 近日某校援引条例,关闭了所有未经审核IP的TCP端口(尽管如此,那些常年暴露着弱密码登录端口的服务器却仍然门户大开,令人感慨)。尽管仍然能够从校内访问,但这一下子给一些那些常年开着远程桌面、安全shell的网络环境破坏分子宣判了死刑。这些人并不把网络作为优质信息的交流工具,而是动起了公器私用的歪脑筋,妄想随身带个平板电脑、手机,通过远程连接就能随时随地享受寝室里配置顶尖但很难移动的工作站、游戏本体验。 但有着悠久历史的交大葡萄却面临了灭顶之灾。当然,生活在移动互联网年代的新交大人们可能很难理解交大葡萄这个PT站是什么(论坛这种过气的互联网产物也已经式微了)。富有版权意识的同学可能会发现,这个网站似乎并不太尊重作品版权,但由确确实实是运行在交大官方的网域和服务器上的。我们希望以解析这次某校关闭TCP端口的一个可能原因入手,来帮助大家对交大葡萄的诞生与淡出有一定的了解。 互联网 早期的网络通讯是建立在已有的电话网络上的。想必各位都对“猫” modem这个词语有印象。modem是调制解调器,最早的目的,是在电话的声音信号与计算机网络的电子信号间转换。这样信息就可以在电话线上传输。早期美国大学间建立的消息网络,就是通过每天晚上固定和其他几个大学拨上几个小时的长途来传达(因此实验室的电话费很吓人)。 随着计算机的进一步普及,电话公司就占据了得天独厚的优势。这和电话的布设过程非常相似:以前要想打电话,得在每对打电话人之间两两牵一根电话线,而电话公司则起到中转的作用,只需要每个人和电话公司牵一根电话线,就能够和所有与电话公司相连接的人打电话。而以前通过一对一的网络业务也类似延展到用户——电话公司的网络连线:只需要与电话公司的中转服务相连,就能访问所有连接到电话公司的网络。美国的美国电话电报公司、中国的电信联通移动都做这个服务,大家平时在家上网,也应该知道自己用的是哪一家的宽带。 使用过拨号上网的同学都知道,以前上网得拨一个实实在在的电话号码。(169、 163、 96163等)这些就是不同电话公司提供的网络中转业务。当时拨号上网的话,不仅网费高,电话还会占线。后来这些电话公司引入了话音分离等技巧,才能实现边上网边打电话。 得益于我国对于网络基础设施建设的重视,上面提到的以电话线网络为基础,以一个划分出来的小部分(通常是高频率段)提供网络服务模式逐渐被以光纤通信为基础,以一个划分出来的小部分提供电话服务的模式所取代,大家的网速也与日俱增。小学的时候玩穿越火线,一个2GB的更新得下载三个小时,而现在的网速完成这样的更新已经几乎不用等待了。 教育网 大家家里的宽带基本上都是电信联通移动网通这些传统电话服务提供商(以及广电、铁通、长城这种二级服务提供商)。但稍有用心的同学就能发现,我们学校使用的并不是上面这些提供商提供的服务,而是“教育网”。 教育网建立于1994年,是为了满足高等教育与科研的需要而建立的第一个全国性TCP/IP互联网。而各大电信服务运营商开始接入互联网,是在1995年。教育网作为国家网络基础设施建设的先锋,近年来也承担了IPv6的试验与普及任务,但因为其目的的局限性,处在一个相对独立的状态,对于准入条件等限制更高。 我们仍然以电话为例。我有一个移动的号码,给电信的号码打电话。语音会先传到移动的交换机,再通过移动与电信之间建立的一条跨运营商交换通道,再通过电信的交换机传到电信的号码。这就涉及到付费的问题:收话费都是移动在收,但是在电信那部分的运营成本怎么办?因此运营商引入了“网际结算”:移动向电信转发,要给电信付钱。早期的网费是包在电话费里面的,网费自然也得按此付费。 这个费用通常并不重要,因为有移动向电信打电话,也有电信向移动打电话,两项的结算基本等同。为了打压其他的市场竞争,尽管通信费用一降再降,这个结算费用并没有降低,这样新的竞争者想要进入这个市场,为了获取已有市场的网络服务资源,就要付出非常高的结算费用。 教育网倒是五雷轰顶,本来就是一个公益性的网络,也不能直接放开商业化,主流的网络服务资源又都在三巨头手上,网际结算费用就变得非常夸张。当时的网络环境实际上基本缺乏版权意识,大家下的都是盗版电影游戏,同质性比较强。教育网网内流量的成本远远低于网际流量成本,因此各大高校受到鼓励,开始搭建PT站。交大也搭建了类似的网站交大葡萄。 交大葡萄 PT,或者说BitTorrent技术,是怎样缓解教育网的流量压力的呢? 现在我们上爱奇艺,视频的内容是存储在爱奇艺的服务器上,并由爱奇艺的服务器传给你的。但是葡萄不同。他不保存视频内容本身,只保存一个列表:谁保存了这个视频的哪一部分。当用户想要下载视频时,就会咨询葡萄服务器谁有这个视频的这个部分,并从有这个视频的另一个用户处下载。相比于公开的BT服务器,葡萄是私有服务器,他还会记录用户的上传量和下载量,并限制那些只下载不传输给其他用户的“坏”用户。 葡萄的用户,或是说教育网PT站的用户,(在当时)自然是教育网网内用户较多。这些用户成为了隐形的“网络资源”,对于那些不在教育网环境下而从教育网下载的用户,就能够平衡一部分网际结算流量。更重要的是,文件可能只需要下载一份,就能够通过上传而分享到整个网络,这对于降低网际请求是天大的好事,而对于带宽本就不足的高校,也能够享受较快的内网传输便利,是一个双赢的过程。截止当前,葡萄的总数据量是113PB,上传下载比是60倍以上,对于带宽的影响是可观的。 TCP阻断 TCP/IP协议栈是葡萄下载的基础。这是一种网络通讯协议,为复杂多变的网络连接环境提供了一个相对稳定的连接基础。我们仍然以打电话为例子。IP协议规定了一种电话号码(地址)——分机号码(端口)结构,而TCP协议则可以理解为建立在IP协议上的一种电话礼仪。通常人打电话的礼仪是: 打电话的人说“喂” (SYN) 对方听到后回“喂,你好,这里是鲍勃” (SYN + ACK) 打电话的人说“你好,这里是爱丽丝” (ACK) 然后开始对话。这被称作TCP协议的三次握手。此次对于TCP协议的阻断相当常见,想必经常上一些学术网站的同学也深有体会:接线员会全程听所有人的电话,一旦听到打进来的人只说一个“喂”的就直接挂断,只允许从里面往外面打电话。而这对于葡萄来说则是遭到了灭顶之灾:这意味着,校外将无法再从校内的同学的电脑上下载到他们想要的文件。 通常运营商也会阻断一些TCP的端口,但通常都是为了合规与安全的目的。比如阻断25/465端口,防止恶意计算机投递垃圾邮件,破坏运营商网络;阻断80/443端口防止私自搭建非法网站(网站备案制);阻断445端口以预防ipc$攻击等。但对全部TCP入口进行阻断,这一看似令人不解的行为,客观上能够降低葡萄带来的流量,为全校的普通网络使用者提供更好的网络体验。 随着带宽的单价越来越低(包括学校买了一条联通线路,也减少了对教育网的带宽需求),工信部去年出台了相关规定,取消教育网与主要运营商网络的网际结算费用,教育网的主要矛盾从网际结算变成带宽本身的成本了。交大也在推进校园千兆网覆盖,但想必大家也都能够察觉到,除了校内几个测速网站能结结实实跑满千兆,一旦跨网最高也就150-200M左右(~20MB/s)的网速(还是加速缓存服务器修到出口跟前的结果),教学区的网越来越慢,几个大教室甚至连8M(~1MB/s)的网速都无法保证。这使得原来双赢的葡萄一下子变成了带宽累赘。由此看来,阻断所有TCP端口,一下子变得合理了。 黄昏 葡萄本身也面临着很多问题:随着旧的葡萄用户毕业,葡萄不可避免地会增加更多的内外交换流量;nexusPHP也使用了目前看来非常陈旧的技术栈,缺乏更新和维护;葡萄自己也面临着侵犯版权的问题。不少学校的PT站已经关闭,或者转向纯IPv6这一目前还是免死金牌的新技术,但这一注定会淘汰的旧互联网产物,自然无法与运营精良、版权丰富的新时代互联网资本抗衡。飞鸟尽,良弓藏。狡兔死,走狗烹。葡萄作为特定历史时期的产物,自然有退出历史舞台的一刻。 想必葡萄最终也会变成comic这样,只剩下一个空壳和一个测速网页的怀旧网站吧。看了看现在赫然1PB大小的种子总量,如此的庞然大物因无法承受自重而一步步走向毁灭,也令人唏嘘。

2021/4/22
articleCard.readMore

调试微信内置浏览器

微信内置的浏览器在不同的平台下的实现方式不同,但都接入了微信自身的OAuth机制用于鉴权。有些时候但微信本身的调试接口藏得很深,有些实现也没有提供一个调试窗口。此处我们以获取调试中的cookie为例来探讨不同设备的异同。 X5内核 在国内下载的微信使用的是腾讯的X5内核。X5内核自带调试功能,可以由以下方法开启: 在微信中访问 http://debugx5.qq.com ,在X5调试页面开启TBS内核Inspector调试功能。 如果提示您“非x5内核”,此时您的微信应该使用的是系统原生框架。您可以参考下一部分的“系统原生框架”进行调试,但也可以在微信中访问debugmm.qq.com/?forcex5=true切换为X5内核。(访问debugmm.qq.com/?forcex5=false就可以切换回WebView,但可能并不完全。) 系统原生框架 iOS和Google Play版本的微信在浏览器实现上调用的是使用系统提供的框架。 因为Android自带的WebView调试需要在打包时传递测试参数,而iOS则需要测试证书才能够调试,对于我们此处的例子是不适用的。 最简单的方法当然是安证书抓包。但是因为一些原因不方便抓包的时候,我试验成功的办法,是: 生成自己的根证书并对需要调试的网页进行自签名。 在手机上安装此根证书。 在电脑上搭建对应网页的服务器,目的是接受手机端传入的cookie,再配置自签名的证书。 在电脑上搭建DNS服务器,将欲调试的网页的DNS结果转发到上述网页服务器上。 配置手机使用电脑上的DNS。 此时就可以接收到cookie了。 后来观察到,当我试图在使用x5内核的设备上修改DNS服务器的时候,chrome已经可以工作,但微信并未生效,一直超时。后来发现x5内核使用微信自己实现的DoH,整个过程中只会查询微信自己的系列DoH服务器域名。而系统原生框架则不支持相关操作。

2021/1/23
articleCard.readMore

欧悌甫戎篇

单独抽出这一篇来看,欧悌甫戎篇实际上可以说是很有趣的一篇哲学“相声”。可怜的神学专家欧悌甫戎和苏格拉底同有官司在身,他们在法庭门前相遇。欧悌甫戎想要控诉自己的父亲杀人。控诉自己的父亲通常被视为不虔诚,而欧悌甫戎有自己的看法。这个理论依据是朴素和经验主义的,来源是宙斯自己都推翻了父亲,所以自己告发父亲并非不虔诚。 唯一该考虑的是杀得有没有理;……如果你明知他杀了人还和他伙同一气,不把他告到法庭来清洗自己和他的罪过,那就和他同罪了。 他们说它并没有杀人,即便杀了,死者既是凶手,我就没有必要为这样一个人去费心上诉,因为儿子告父亲杀人是不虔诚的。 从这里可以看出,欧悌甫戎的目的不是去将父亲判罪,而是要证明在虔诚上“惩治杀人犯”高过“控告父亲”。欧悌甫戎是个所谓精通神学的人,他希望通过这次审判,将自己立于一个虔诚的位置,审判的目的并非让自己的父亲服刑,而是希望通过这次审判证明自己的虔诚观念,并且洗刷自己不虔诚的罪恶感。即便自己在法庭上落于下风(这是几乎肯定的,在开庭之前他就已经被苏格拉底驳倒了),也可以在道德上落于不败之地。 苏格拉底啊,他们根本不了解那判别虔诚和不虔诚的神意。 苏格拉底诱导他对虔诚与否下一个定义。这就把号称自己精通神学,却只是精通神话故事的欧悌甫戎带到苏格拉底的逻辑陷阱里面去了。 显然欧悌甫戎提出的第一个定义“神所喜爱的就是虔诚的,神所不喜爱的就是不虔诚的”在多神论的混乱希腊神话中有语病。在苏格拉底的引导下,他给出了完备的第二个定义“虔诚就是神所一致喜爱的”。这个过程顺水推舟,可以看出欧悌甫戎除了神话故事外对基本的逻辑与辩术一无所知,正是苏格拉底的“认识你自己”的好对象,整篇对话被苏格拉底牵着鼻子走是理所当然的。完成了这个命题的声明,苏格拉底也就点到为止,没有继续做文章。 其实这个问题可以更进一步,人之间的分歧归求于神,而神之间也存在同样分歧。诸神这样的分歧,虔诚与不虔诚之间的混沌,诸神之间的理念争端(比如弑父到底虔不虔诚)显然需要求于一个更高层次的统一体。宙斯虽然是所谓的众神之神,但是希腊人民丰富的想象力早就把宙斯在诸神中的公信力败坏无存了,把多神转成主神在雅典是行不通的。对于更加抽象的统一体,显然柏拉图对此有一个“至善理性”、“绝对真理”一类的答案,但苏格拉底显然不会在此点破,否则自己也会落入渎神的境地。 苏格拉底把话题岔开,开始用主被动的关系探讨虔诚与神喜爱的事物之间的关系。这是个非常有趣的话题,甚至近于诡辩。我读到一句评论“苏格拉底需要的不是明智,而是让明智登场的舞台”,此处即是体现。“事物因为被神喜爱所以成为神喜爱的”,和“事物因为是神喜爱的所以被神喜爱”,这样两句话似乎有语义重复,怎么讲都应该是对的,但是两者是完全不同的。这是个先后问题,而我们显然给不出一个神的视角下的答案。(或许这个内容在客观唯心的观念下可以找到一个比较好的解决,从客观理性的绝对唯一精神里产生出一个服从其意志的希腊式神祗)苏格拉底利用这个漏洞,把神喜爱的替换作虔诚,问题一下子就展现出来了。 这个问题当然难不倒写书的柏拉图。这个神层面的问题反而强化了他们人依理性而非神谕行事的信条,自然,理性是一切道德的源头,而希腊诸神都是其派生物,先后关系就自然解决。但是在基督教的背景下就不太圆得过去。上帝作为唯一至善的神,也是道德之源,自然就产生了上帝无能的困境。尤其是近代的残酷战争,上帝知善而为之和上帝为之而称善的选择,在毒气战、细菌战、犹太人屠杀面前都不能自圆其说。苏格拉底的理性“真神”并不能够拥有喜怒哀乐这样的人类情感、喜好,人的事务和(希腊)神的事务是相分离的。这直接从原始的泛神论升到了一个较完善的客观唯心的思想层面。 另外,有一些观点认为,这里的苏格拉底只是顺着欧悌甫戎的假设往下说,即只说明了虔诚就是神喜爱的这个观点的问题。如果我们把上面的问题视作是主动与被动的统一,被喜爱和喜爱是同时被定义的(如果换成某人喜爱和被某人喜爱,我觉得这里的问题是平凡的),那么这样一个论证只能说明虔诚不能和神的喜好等价。这并不指虔诚不能和其他任何东西等价,或者虔诚是个伪命题。 显然,我们的神学家欧悌甫戎没有找到这样一个颠覆神学的答案。他只有将其述诸诡辩,但话又确实是自己嘴里说出来的,被苏格拉底揶揄一通。 苏格拉底又开始试探虔诚和公正的关系。苏格拉底把虔诚在公正上绕来绕去,从欧悌甫戎的嘴里得到了“虔诚是一种对待神的公正”,转头就用狗和猎人、牛和牧人的例子类比,人们去“对待”一个事物,是想要给这个事物带来益处,得到虔诚就是给神灵好处,但是神灵并不能因此而得到好处。苏格拉底还是留了个台阶,欧悌甫戎马上改口说“这种公正是对神灵的服侍”。但是苏格拉底又用将军、农民类比,神要完成“又好又多”的结果,并不需要人这样的仆人。 类比实际上并不是一个逻辑上完备的证明方法。此处的类比,至少是建立在神是可以类比的基础上的。在两位的理解内,古希腊的神是拟人化的神,有人的喜怒、好恶、优点陋习,和人之间的界限大概只有血统。如果能够跳出可类比这个基础,直接否定神与普通事物的相通性,比如说,神是形而上的,对神的服侍与对人的服侍是完全不同的,苏格拉底的类比就不攻自破了。当然,希腊人的神话并没有将神与人的关系抽开。欧赫迈罗斯认为神祇来源于历史上真实的英雄,整个希腊神话对于弑父、食子这些黑暗话题也并不避讳,和亚伯拉罕诸教天选那一套完全不同。 欧悌甫戎连忙找到一套理论把这套服侍的话术(也就是虔诚和公正是包含关系)搪塞过去。苏格拉底接下话来,给出虔诚是关于祈祷和祭祀的知识,直接推出虔诚就是人神交易。但是神灵又不需要人的这种礼物。欧悌甫戎想要否定这种交易的观点,又没什么话,说神灵通过人们的尊荣、崇敬和满意来得到好处。这又把虔诚和神喜爱的东西联系起来了。自己的逻辑被全部掀翻,欧悌甫戎连忙有个急事,先走一步。 如果按照整个来看,欧悌甫戎篇是柏拉图对话集的开篇,也是通常意义上的欧悌甫戎——申辩——克里同——云四篇的第一篇。紧接着就是申辩篇,在这一篇中苏格拉底需要为自己的渎神,也就是不虔诚的罪名辩护。一些观点认为,这一篇是在阐述苏格拉底的虔诚观,来为后文的申辩铺垫。但是这里的苏格拉底显然并没有给出这样一个明确的虔诚观念,或者说,执笔的柏拉图在这个时候还给不出所谓的虔诚究竟是什么。申辩篇里对于渎神这一点也不太明确,苏格拉底用神谕把这个责难搪塞了过去。 苏格拉底对于希腊诸神的态度实际上比较调和,他也会像其他雅典公民那样献上祭品,也遵神谕(尤其是德尔菲神庙的那条),但他并不像普通的雅典人那样,将神与自身的生活完全绑定。而只有这种绑定才有虔诚的概念。苏格拉底提倡的理性与相信的“灵机”,更有一种“凯撒的归凯撒,上帝的归上帝”的人神分离原则。神的祭祀只能决定神的事务,而人的行为需要从理性出发,去追求从理性生发出来的美德。神从赋予人理性的时刻起,人就是一个在理性的指引下行动的独立个体。这样应该就没有所谓的虔诚可言了。 Cf. [1] <游叙弗伦篇>读书笔记 douban [2] 《游叙弗伦篇》变奏曲 douban [3] 习读柏拉图札记——《欧悌甫戎篇》 douban [4] 一无所知的人哪 douban [5] 游叙弗伦困境 Wikipedia [6] 苏格拉底的灵机 [7] 论苏格拉底的神 [8] 2016.Feb<Plato's Euthyphro> (Kiki读书讨论后记) douban

2020/6/29
articleCard.readMore

苏格拉底的申辩篇

问与答 Q: 苏格拉底在公元前399年被审判,并被判死刑。那时苏格拉底已经70岁了,他是什么时间出生的?苏格拉底跟孔子是否生活在同一个时代? A: 苏格拉底在前470年出生。孔子死后九年苏格拉底才出生,但是两者时代基本相近。 Q: 通过阅读《申辩篇》,雅典的法庭审理制度是怎样的?有什么样的程序安排? A: 对于苏格拉底受到的指控没有具体的法条,对此法庭审理分为两次,由501名雅典公民组成,按照投票表决判断。第一次表决判断是否有罪,第二次表决决定使用怎样的刑罚。 Q:在为自己申辩的时候,苏格拉底把自己比喻作什么?在辩护的时候,苏格拉底认为自己应该怎么样被处置? A: 苏格拉底将城邦比作太大太非的骏马,而将自己比作叮马、使城邦与公民焕发精神,而不至于被自大的愚蠢蒙蔽的牛虻 苏格拉底认为应该请自己在国宾馆用餐以报答他的功劳。苏格拉底拒绝监禁、放逐与禁言,并建议罚款。 Q: 苏格拉底采用了什么样的方法为自己辩护?这种方法是否成功? A: 苏格拉底采用了归谬、归纳和比喻的方法为自己辩护。显然这些方法并不成功。最后苏格拉底以牛虻自比,坚持自己无罪,应该被嘉奖,激怒了陪审团,支持苏格拉底死刑的人反而增加了。 Q: 苏格拉底被审判时的罪名是什么? A: 苏格拉底被指控渎神与腐化青年。 Q: 简单地概括苏格是如何为自己“腐蚀了年轻人”这条罪名进行辩护的? A: 首先苏格拉底通过归谬反驳了梅雷多认为的只有苏格拉底祸害青年与苏格拉底行为故意的定论,然后通过归纳反驳了对自己不信神的指控,进而反驳了苏格拉底教坏年轻人渎神的指控。 Q: 在苏格拉底看来,死亡是什么? A: 苏格拉底对死亡报以积极态度。他认为,死亡要么是一了百了绝对的虚无,要么是灵魂的转移。而前者好过大部分的生活,后者则是真正的幸福。 苏格拉底与民主 我觉得苏格拉底其实并不支持民主的制度,这次审判应该也可以说苏格拉底牺牲自己,当“戊戌六君子”,来给雅典的全体公民上一课,告诉雅典的公民这种民主制度是有缺陷的。我认为的苏格拉底在政治上应该是更支持雅典之前实行的寡头政治,精英主义政治。我读到其他的一些观点中称,苏格拉底被审判的原因并不是渎神和腐化青年,而是因为和之前三十人寡头僭主有师徒关系,而被政治处决。也就是说,苏格拉底不论如何都会被处死。就算苏格拉底博得了很多陪审团的同情,但整体判处苏格拉底死刑的票数都占多数。 苏格拉底自己也提到,会有人拉着自己的妻子儿女来为自己拉票,也从一开始就表达了对于这种民主制度的修辞、诡辩的嘲讽。再者苏格拉底自身也有很多学生在审判官里,苏格拉底并不是不知道这种审判制度的愚蠢,糊弄糊弄就能脱罪,最差也是罚点款。我觉得在这一点上苏格拉底应该可以说在用自己的生命来证明这种制度的不可靠。苏格拉底在第二轮审判之时都还在嘲讽陪审员,激怒这些陪审官。苏格拉底认为自己的任务是揭露别人的无知,这一次是苏格拉底在揭示民主制度的愚蠢。 我们看到,苏格拉底讲理与思辩压倒了原告,却仍然被这项民主制度处决。这就是民主的无知。雅典人并不像现在很多民主国家那样把民主看作他们政治的根基。雅典从贵族——奴隶制度开始,民主时期实际非常短暂。整个希腊的政治学说也五花八门。苏格拉底死亡之时正是雅典民主衰落之时。雅典的民主制度是由罗马进行改良与发扬的,而罗马的民主维持时间也非常短。实际上现代的民主政治,从光荣革命开始,就是披着民主皮的精英政治。法官、律师、法考、法条,理性、客观的审判与被审判群体,这些才是苏格拉底想看到的。 我们现代的苏格拉底可能并不能成为一位“苏格拉底”。美国的辛普森案实际上就是一个例子。尽管美国的民主制度已经经过非常完善的改良了,但是辛普森案还是揭露了陪审团制度的缺陷。但问题是,我们是以一个后来者、从柏拉图的讲述的角度来讨论苏格拉底,而不是以五百人团的身份来讨论苏格拉底。现代的“苏格拉底”可能早就被恶意的公众舆论所淹没了,我们却不自知。

2020/6/24
articleCard.readMore

LCA 最近公共祖先

最近公共祖先(Lowest Common Ancestor)的定义:同一棵树的节点u,v,定义: lca(u,v)为分别包含u、v的全部该树的子树的并集中的最小高度树的根节点。 下面介绍四种求最近公共祖先的方法。暴力法、倍增法、Tarjan算法,RMQ算法。 暴力法 标记现在的点访问过;向上跳到父节点。 重复这一过程,直到跳到的父节点被标记为访问过。 时间复杂度O(n)(还需要O(n)清除标记)。 一种不需要清除标记的优化方法:将访问标记和查询次数对应:第i次查询标记i,这样就可以不需要清除标记。 DP标记——倍增算法 标记一组f[i][j],标记i节点的上方第$2^j$个节点。 深的节点向上跳,跳到相同的高度。 可以利用DP标记向上跳:比如说深度相差7,分别跳4,2,1。 同时向上跳:同时跳到最深的不同节点,直到跳到父节点相同为止。 时间复杂度:O_{init}(nlgn),O_{query}(lgn), 空间复杂度:O(nlgn) 这是一个在线算法。 Tarjan算法 将所有的查询lca(a,e),lca(c,e),lca(d,a)建立查询集合 1 2 3 4 5 6 7 Sa={(a,e),(d,a)} Sc={(c,e)} Sd={(d,a)} Se={(a,e),(c,e)} 维护并查集UFS(u)->u 一遍DFS,完成所有查询。 进入节点u:对于查询集合Su中的每一个查询lca(u,v): 如果查询没有访问过,标记被访问。 如果查询被访问过,lca(u,v)=UFS(v) Union(UFS(v),UFS(father_v))->UFS(v) 这是一个离线算法。 此处的UFS为并查集:定义操作:查找find(u),并union(u,v):u<=>v。 时间复杂度:O_{init}(n+m),O_{query}(n+m(lgn)), 空间复杂度:O(n) RMQ 区间最值查询算法 区间最小值查询(Range Minimum Query)的定义:对于数组list, rmq(l,r) = min_{l<=k<r} list[k] RMQ: Sparse Table算法 建立数组st[i][j]为区间[i,i+2^j)的区间最小值下标。 (可以递推:st[i] [j] = min{st[i][j-1],st[i+2^{j-1}][j-1]}建立) 询问:设t为r-l+1的二进制最高位数,则有 rmq(l,r) = min{st[l][t], st[r-2^t+1][t]} 因为上面的集合一定覆盖[l,r]。 用RMQ解决LCA 先DFS一棵树,记录DFS顺序表为seq。 DFS顺序:DFS搜索所经过的所有节点,包括已经经过的重复节点。(长度2n-1(参考电路理论的树)) 用first[node]记录节点node在第一次出现的位置。 此时lca(u,v)=seq.rmq(first[u],first[v])

2020/6/3
articleCard.readMore

弥罗斯人的辩论

这篇文章是《古希腊文明演绎》课上的读后感[1]。 这次谈判没有赢家。双方都没能达到各自的目的,甚至连可以双方所构想的让步都没能实现。 雅典的武力威慑是完全无效的,因为所谓威慑在于武力的将实施,而雅典人最后只能动用了武力,杀光所有适龄男子,孩子妇女卖成奴隶,完全相悖于最初的将弥罗斯变为一个殖民地的目的。而弥罗斯人也没能实现事态的和平解决与正义,遭到毁灭。 这次谈判陷入了所谓的“a dialogue of the deaf”。双方只是在谈判中交换了意见,鸡同鸭讲,而没有出现任何余地的妥协。这样的谈判就如修辞的辩论一样,双方都设定了唯一不变的基点,这不是谈判。谈判的目的是达成共识,方式是双方的妥协。如果双方都各自把谈判的终点设在自己的起点,而不是一个折中的、双方都能妥协的位置,等待着对方放弃一切主张来靠拢,这样就不需要谈判了。 结果的形成和教训 出现这样的无效谈判,作为谈判的主导方的雅典显然应该负有主要责任。雅典人企图在弥罗斯人中间获取利益,杀光弥罗斯人,自己派人殖民是雅典人不希望看到的。但他们全程都是在以一个所谓明理的旁观者的角度发言,他在谈论武力差异的绝对,谈论雅典的强大与无畏,谈论救兵到来的希望渺茫,奉劝识相的弥罗斯人快快屈服。但是雅典人本来就是携军队前来,地位从一开始就是不平等的。而这种发言展现出来的傲慢,直接排除了弥罗斯人的妥协可能。 弥罗斯人在这次辩论中是被动一方,但并不代表弥罗斯人无法改变辩论的局势。弥罗斯人希望保全自己的政治存在与人民,但弥罗斯人也在以一个旁观者的角度发言,谈论虚空的正义和国际影响,谈论入侵弥罗斯对雅典可能带来的坏处。弥罗斯人说: 我们做奴隶,而你们做主人,怎样有同样的利益呢? 从此完全否定了雅典提出的屈服方案,而且除了自己的自由中立没有任何的妥协余地。 可以肯定的是,弥罗斯人根本没有能力自己打退雅典人,他们谈判的基础是斯巴达的援助、神灵、正义与非正义,和其他中立国的态度的转变可能。雅典的大军已经停在弥罗斯岛旁了,而弥罗斯人还在幻想着 那么,你们不赞成我们守中立,做朋友,不做敌人,但是不做任何一边的盟邦吗? 双方都没能抓住对方的利益点,结果自然也好不到哪里去。 谈判的目的是消除隔阂、解决问题,在这样一个强弱悬殊的谈判中,如果弱势方弥罗斯人能够为雅典人指出一条既能满足雅典进攻跳板、朝贡纳税的目的,又能够保存自己的政治和人民相对自由和独立的道路,江东子弟多才俊,卷土重来未可知。谈判所需要考虑的,不是自己需要什么,而是别人需要什么。 弥罗斯人和雅典人 弥罗斯人显然站在正义的一方,但是历史不会偏袒正义,弥罗斯年底就被雅典人攻破,人要么杀要么卖作奴隶。弥罗斯人似乎也只有在谈判场上斥责对方的无耻了。但妥协显然也不是什么好的解决办法。“今日割五城,明日割十城,然后得一夕安寝。起视四境,而秦兵又至矣。”二战的时候,法国人妥协,被德国吃干抹净,波兰人反抗,照样被德苏吃干抹净。由此看来,雅典人所说的 正义的标准是以同等的强迫力量为基础的;同时……强者能够做他们有权力做的一切,弱者只能接受他们必须接受的一切。 倒是有点道理了。弥罗斯所遭遇的,只能说是弱小国家的必然结局。现实中是没有主持公义的神的。 雅典人是愚蠢的。弥罗斯一开始也并非雅典的敌人,雅典人将弥罗斯纳入联盟的目的也并不是为了单纯的占领。本可以通过谈判团结的中立势力,因 雅典人对他们施用压力,把他们的土地蹂躏,他们才公开地成为雅典的敌人。 从而又需要耗费人力精力去剿灭弥罗斯。派两个傲慢自大的将军去参加战前辩论,与其说是战前辩论,还不如说是战争预告。武力是愚蠢的人解决问题的最终手段。 当今世界的强权国家似乎和雅典人有着一样的看法,原本建立的世界谈判桌被玩成了战争借口的制造工具,而如伊拉克战争,联合国否决后,美英直接绕过联合国决议自组联军。双方实力的差距越大,谈判的效用就越弱。或许,弥罗斯人的辩论能够带给我们最大的启示,就是不要对与实力更强的对手辩论能够解决争端抱有太大信心。 本文是该课程的一次作业。 ↩︎

2020/6/2
articleCard.readMore

埃斯库罗斯作品:波斯人、阿伽门农

这篇文章是《古希腊文明演绎》课上的读后感[1]。 波斯人 波斯人以波斯国王塞克塞斯带领的波斯海军中计败于雅典军队为故事的背景。故事由两个部分组成。第一个部分中,波斯军队讨伐雅典,迟迟没有军信回报,而波斯的太后、大流士的妻子阿托萨做了噩梦。通过阿托萨与歌队的对话,交代了波斯军队的情况。 此时,军队信使传回了悲报:强手云集、人数众多、船坚炮利的波斯军队全军覆没,而雅典人未动分毫。究其以少胜多的原因,是希腊人使了诡计,欺骗塞克塞斯希腊人人心涣散,都在逃跑。塞克塞斯因之前成功搭好浮桥,以为有天神相助,未加细想就命军队成合围之势。第二日希腊人趁机反攻,海战楞头青的波斯船只乱了阵脚,如赤壁之战的曹军一样,互相碰撞沉没。本等候胜利、在一旁待命的陆军也被雅典人的反攻打退,将士死伤无数,逃兵也大多丧命。 第二个部分开始。战争的悲报让波斯人心涣散。阿托萨和歌队召唤大流士的亡魂,大流士听毕状况的说明后,心痛万分。大流士强调敬神的重要,并指出塞克塞斯已经归来,衣衫褴褛。塞克塞斯失魂落魄,悲痛万分。自己为国家招致灾难,将领兵士也为之丧命。故事在悲哭声中落幕。故事除了阐明敬神的重要性之外,还警示我们不可让理智被心灵所压过。 歌队是这部戏剧不同于现代的戏剧的一点,也是古希腊戏剧中的一个核心组成部分。歌队没有人的特征——只是一个功能性的群像,又不仅仅是一个用于交代背景的纯功能性的旁白。在大流士鬼魂出现和塞克塞斯两个部分,歌队也参与了人物之间的对话。而歌队与塞克塞斯 塞 我看见大祸临头,撕破了我的王袍。 歌 哎呀!哎呀! 塞 放出那更悲痛的声音! 歌 两声不够,再唤一声。 的对白,很有钦差大臣里对观众“你们笑什么?笑你们自己”的意思,歌队又成为站在舞台上的观众了。 阿伽门农 阿伽门农是埃斯库罗斯的作品,以特洛伊战争结束,阿伽门农返回阿耳戈斯为背景。故事以守望人得到阿伽门农攻下特洛伊的消息开场。歌队进场,介绍特洛伊战争的背景,墨涅拉俄斯的妻子海伦被特洛伊王子帕里斯拐走,因此他和他的兄弟阿伽门农起兵远征特洛伊。在远征的途中,神发出逆风阻挡了阿伽门农的船队。而阿伽门农为了讨好神求顺风,非法地献祭了他的女儿。此时,王后克吕泰墨斯特拉告知歌队守望人汇报特洛伊城被攻陷的消息。在歌队为此喜悦的同时,王后担心如果军队败坏纪律会招致厄运。歌队称赞王后的冷静谨慎,开始吹捧宙斯的英明,责备特洛伊王子和海伦因为私情挑起了战争。 传令官宣告阿伽门农得胜归来。说阿伽门农借宙斯的大能夷平了特洛伊城,但返程时遭遇了可怕的风暴,墨涅拉俄斯的船队在风暴中漂散,不知去向。歌队再次谴责挑起这次战争的海伦。此后阿伽门农和俘虏的卡珊德拉上场。他赞美阿耳戈斯军队的强大,并歌颂阿耳戈斯的护卫神。王后上场,假意迎合自己的丈夫,表达对于阿伽门农的思念与阿伽门农凯旋的欣喜。她命令婢女们用紫色花毡铺一条路,请阿伽门农从毡子上回宫。她说: 至于其余的事,我的没有昏睡的心,在神的帮助下,会把它们正当的安排好,正像命运所注定的那样。 阿伽门农起初对在紫色毡子上走感到恐惧,担心冒犯天神。但王后迎合阿伽门农胜利归来的心态说服了他这样做。阿伽门农仍然担心被神嫉妒,因此踏着紫色花毡光脚走进王宫。王后道: 啊,宙斯,全能的宙斯,使我的祈祷实现吧,愿你多多注意你所要实现的事。 歌队暗示福兮祸之所倚。王后上场,宣布了自己国家体贴的奴隶政策,请卡珊德拉也进宫。但卡珊德拉不肯下车,王后和歌队都劝说她下车进宫,也不起作用。王后放下狠话,独自进宫。卡珊德拉预言自己的毁灭,指出这个家庭不敬神,充满了家人间的杀戮,并预言会发生一件莫大的祸事,而能够帮上忙的人又远在天涯。歌队先开始不理解,但结合城中沸沸扬扬的传言和卡珊德拉对妻子杀死丈夫的明示,而自己也很快死亡的预言才明白即将到来的灾难。 阿伽门农被刺。歌队听到了国王的叫喊,却在怎样救国王的争执中错失了救治的机会。此时 后景壁打开,壁后有一个活动台,阿伽门农的尸体躺在台上的澡盆里,上面盖着一件袍子,卡珊德拉的尸体躺在旁边。 王后站在台上,说她用长袍罩住阿伽门农,刺了阿伽门农三剑。王后说明,刺杀阿伽门农的原因是阿伽门农侮辱了自己的妻子,像献祭牲畜一样献祭了自己的女儿,应该为此付出代价。歌队哀叹说,国王真是不幸,为了一个女人去打仗,现在又死在另一个女人手里。 自己的兄弟被阿伽门农的父亲、自己的叔叔杀死来宴请父亲的埃癸斯托斯如愿以偿,非常高兴。歌队对此非常愤怒,双方按剑,此时王后出来,制止了这场争斗。 阿伽门农的悲剧性,更多体现在人物在道德困境中的挣扎上。克吕泰墨斯特拉的女儿被自己的丈夫无辜、非法地杀害。一方面是丈夫远征特洛伊胜利,一方面是自己为女儿的复仇,建立了克吕泰墨斯特拉的悲剧形象。阿伽门农在剧中可能更多是以一个背景与复仇的结果而存在的。他还没怎么说话,就被杀死在浴缸里。而阿伽门农作为伟大的胜利者,丧命于自己妻子的刀下,更加深了王后此举的冲突。 在英雄时代的宏大叙事下,王后用自己的决断与独立完成了自己的私人复仇。尽管通常来讲克吕泰墨斯特拉都被描述为伙同情夫杀死丈夫的恶妇,但《阿伽门农》中的王后的正面、宿命形象更加突出,更多是一个被不可抗拒的宿命所左右,只能以这种方式反抗的弱小角色。 本文是该课程的一次作业。 ↩︎

2020/5/19
articleCard.readMore

电路理论资料

网课 电路理论 01020304050607080910 11121314151617181920 21222324252627282930 31323334353637383940 41424344454647484950 51525354555657585960 6162 电路实验 0102030405060708 习题与答疑 第一次习题课 第二次习题课 12 第三次习题课 4.19电路答疑 12 5.17电路答疑 5.24电路答疑 6.7电路答疑1 2 书籍 需要连接VPN访问,配置方法 电路基础(第二版),陈洪亮、张峰、田社平, 高教社, 2015 电路, 邱关源, 高教社, 2006 简明电路分析基础, 李瀚荪, 高教社, 2002 Fundamentals of Electric Circuits, Charles K. Alexander, 清华大学出版社, 2000

2020/5/5
articleCard.readMore

Coronavirus: A Humanitarian Crisis

[1]Caused by the coronavirus SARS-CoV-2, the still ongoing pandemic have already killed more than two hundred thousand deaths and infected the other three million people worldwide. The outbreak, first identified in Wuhan in December 2019 and declared as an International Public Health Emergency on 30 January, and a pandemic on 11 March by WHO. It's the worst pandemic since modern times. The virus, just like the SARS-CoV-1 in the 2003 outbreak, can spread between people during close contact, mostly via small fatal droplets produced by coughing, sneezing, or talking. Other spreading methods like surface touching have been reported, and there're viruses detected in Paris' water-supplying system. The patient becomes most infectious after three days, without symptoms revealed, making this virus extremely easy to spread without notice. Common symptoms include fever, cough, fatigue, shortness of breath, and loss of smell. Severe patients will suffocate and die, if not taken care with ventilators. The virus has no known vaccine or specific antiviral treatment, and a higher severe rate, causing shortage in medical supplies as masks and ventilators. The pandemic has caused the largest global economic recession since the Great Depression, and humanitarian crisis, as widespread shortages in medical and living supplies, exacerbated by panic buying. Misinformation about the virus has spread online, and there have been incidents of xenophobia and discrimination against Chinese people and against those perceived as being Chinese, or as being from areas with high infection rates. Governments and residents are the two definitive factors of the epidemic situation of a country. Only when they both do their jobs can the disease be under control. Proceed from actual conditions in China and the United States. After a delay caused by short-sighted malfeasance officer, the Chinese government took remarkable strong actions to contain the outbreak. Medical workers were sent from whole mainland China, and Hubei province sacrificed most, with a nearly three months' lockdown. Government set up module hospitals and containment facilities, each for severe patients and mild virus carriers.Chinese citizens in the whole world offered volunteer help to support the containment during the lockdown. Key life-saving industries made their ways to resume production, with their staff volunteered to cancel vacations and work overtime. People gave up their old traditions and stay at home to stop spreading. However, in the United States, though China and WHO have warned about the severity of the virus, US president Trump only cares about his votes. To revitalize the broken economy, Trump ignored the virus and trumpeting on returning to work, until the US becomes the center of pandemic, with one million infections. Today's White House tenants even use the precious epidemic prevention materials, like ventilators, during the epidemic, to suppress the Democratic-led states that not voting him. Trump has even recently made his infamous ridiculous remarks about "disinfectant injection", and the same time, Pompeo is stabbing China, through the fictitious "All China's fault" theory, by deliberately provoke hatred and opposition, and repeatedly undermine international epidemic prevention efforts, to cover up the fact that the US government has no intention to fight the epidemic. These White House tenants stood idly by in the prevention and control of domestic outbreaks in the United States, and ignored the lives of the American people to pursue Republicans' political interests. The United States has the finest medical resources in the world, and top medical research facilities like Oxford and John-Hopkins are located there. And as early as 28 January US CDC have developed the test kit for coronavirus. Despite this, the United States had a slow action to fight the initial outbreak. Testing was heavily delayed by defective test kits produced by the federal government in February, and US goverment is keeping social forces from making test equipments themselves by delaying the issuance of permits. US citizens cannot get the test themselves, until the early March, with a doctor's approve. By 13 March, fewer than 14,000 tests had been conducted. On 22 March, the Associated Press reported that many people with symptoms and a doctor's order still had to wait hours or days to be tested. In the face of pandemic, the United States are deliberately disturbing the entire world's anti-epidemic situation to make the world pay for its own deaths and economic recession. Recently, US secretary of state Pompeo even prompted “cutting off supply” of the WHO. This action extremely harmful to the global epidemic, especially to the least developed countries and vulnerable groups. The epidemic has already caused trauma to all mankind, but the US government continues to attack Countries like Iran and Cuba, by cutting off their medical and finantial supplies, which has led to greater humanitarian crisis. Though the coronavirus epidemic is a worldwide disaster for the entire humanity, we need to keep our eyes sharp to distingush, which countries have made every effort to save their people and even the world from being killed, and who are tying everyone onto his own express of chaos to hell. 这是英语的小论文。 ↩︎

2020/5/1
articleCard.readMore

雨夜

下雨了。 以往的雨使我厌烦。每次穿着鞋子走出去,不知为何,总会把脚沾湿。 因为疫情,呆在家里。 隐隐约约听见雷声。好像又不是雷,是飞机划过雨云的咆哮。 微微的风吹来。 是沾湿的尘埃的气味。 偶尔传来两声早蛙声。风已经不冷了。吹过脖颈,肌肉是紧张的,但终究没有打寒战。 春天来了。 中午午眠之时,钢琴声、钉锤响和孩童的争执闹得人烦躁。 现在也不了。 远方传来笑声,和沉闷在微湿的大气里的狗吠。偶尔划破大气的快车呼啸。 顿时感觉键盘喀嚓声的刺耳。音乐,至少在拉威尔以早的作品,都单薄而乏力。 现在的风有点冷了。 想想,应该是雷吧。不管是不是雷声,再不论声音具体有多大,声音的潜力是无限的。它用它最微弱的声响,提示着我。 又听了德彪西的《夜曲》。每次听到《祭》这一段,都会因奇幻的旋律和乐声的力量而震颤。 这震颤是雷。力量从不源于乐手的汗水和涨红的脸庞。 这是酒神的力量。 这是自然的力量。

2020/4/10
articleCard.readMore

linux配置SSR

注:本文提供的方法因为不接管系统的路由,有些软件用起来会很难受,也不适用于透明代理网关的情形。如果你希望在系统层面,配置无感的透明代理,请参考这篇文章。 引入 SSR是一种代理软件。(如果不知道是什么也没有必要读这篇文章了。)尽管有一些声音声称SSR对信息安全是有害的,流量比较容易被识别,但是仍不能改变现在绝大多数的网络加速服务都是使用SSR作为媒介。 尽管windows端和android端都有比较简单的配置方法,在linux上还是比较困难的。尤其是有些服务商提供订阅链接,有很多节点,且一直在更新,使用命令行化的SSR很不友好,而开一个虚拟机跑又太过夸张了。因此,经过搜索,我找到了较好的解决方案。 Electron-SSR 首先需要安装electron-ssr,在AUR里面有,你可以直接yay -S electron-ssr来安装。但是需要注意的是,因为这个包里面加了130MB左右的electron,安装速度就不敢恭维了。你可以通过重新修改PKGBUILD,更换国内淘宝的cnpm源,来加快(应该是这样?我不知道,傻乎乎的安了一晚上)。 安装了之后才是这篇文章的主题。 配置 当然,你安装完之后就可以看见漂亮而且功能强大的electron-ssr了。功能比单纯的ssr-python多多了,几乎与windows端的功能相同,而且还有electron写的美观的UI。前端的大佬写的就是好看。 但是使用方法并不像windows端那样显然。我开始按照windows那样的使用方法,根本不能连上,以为这只是个中看不中用的花瓶。后来才发现是我的能力问题。 与windows端自动全局代理不同,这个版本的electron-ssr只提供一个SOCKS5转发功能,并不能修改linux自身的代理。你需要在程序的配置界面配置一个转发端口,然后将linux的代理转向这个端口。绝大多数的linux都支持系统层面的SOCKS5代理,你可以直接填 SOCKS5://127.0.0.1:XXXX。 当然,像我这种用Archlinux的,每天都要pacman -Syu,滚一下就是600MB的下载,平时都挂的SJTUG源,长期系统挂着节点太奢侈了,而切换代理又比较麻烦,需要配置文件。因此就利用了默认的electron-ssr性能,即只提供SOCKS5转发,并对一些需要加速的程序进行了配置。 配置git 我不用gitee,通常链接的git是AUR和github的git。而这两个因为CDN的原因都比较慢。 你可以执行: 1 2 git config --global https.proxy socks5://127.0.0.1:1080 git config --global https.proxy socks5://127.0.0.1:1080 来配置。 当然,如果要取消,你也可以执行 1 2 git config --global --unset http.proxy git config --global --unset https.proxy 配置MAKEPKG MAKEPKG没有一个特定的代理配置,因此在打开了以下几个配置文件后(均可,作用域不同),需要单独对命令进行配置。 1 2 3 /etc/makepkg.conf $XDG_CONFIG_HOME/pacman/makepkg.conf ~/.makepkg.conf 打开之后会有这样的文本: 1 2 3 4 5 6 7 8 9 10 11 12 13 ######################################################################### # SOURCE ACQUISITION ######################################################################### # #-- The download utilities that makepkg should use to acquire sources # Format: 'protocol::agent' DLAGENTS=('file::/usr/bin/curl -gqC - -o %o %u' 'ftp::/usr/bin/curl -gqfC - --ftp-pasv --retry 3 --retry-delay 3 -o %o %u' 'http::/usr/bin/curl -gqb "" -fLC - --retry 3 --retry-delay 3 -o %o %u' 'https::/usr/bin/curl -gqb "" -fLC - --retry 3 --retry-delay 3 -o %o %u' 'rsync::/usr/bin/rsync --no-motd -z %u %o' 'scp::/usr/bin/scp -C %u %o') 按照需要添加相应的socks5代理选项如下。比如对于curl执行的配置。(我的代理端口是127.0.0.1:1080) 1 2 3 4 5 6 7 8 9 10 11 12 13 ######################################################################### # SOURCE ACQUISITION ######################################################################### # #-- The download utilities that makepkg should use to acquire sources # Format: 'protocol::agent' DLAGENTS=('file::/usr/bin/curl -gqC - -o %o %u --socks5 127.0.0.1:1080' 'ftp::/usr/bin/curl -gqfC - --ftp-pasv --retry 3 --retry-delay 3 -o %o %u --socks5 127.0.0.1:1080' 'http::/usr/bin/curl -gqb "" -fLC - --retry 3 --retry-delay 3 -o %o %u --socks5 127.0.0.1:1080' 'https::/usr/bin/curl -gqb "" -fLC - --retry 3 --retry-delay 3 -o %o %u --socks5 127.0.0.1:1080' 'rsync::/usr/bin/rsync --no-motd -z %u %o' 'scp::/usr/bin/scp -C %u %o') 取消就直接删除对应的语句即可。 配置chrome 这一步需要你安装chrome的SwitchyOmega插件。 按照要求配置后,就可以正常连接了。当然,SwitchyOmega的功能很多,你也可以订阅一些PAC规则,用autoswitch功能自动加速,这里就不再赘述了。

2020/4/8
articleCard.readMore

二叉堆的实现

我一直以为实现一个二叉堆是很困难的事情,还以为会用到树翻转这种经典梗问题,结果自己实现了一下,发现非常简单,甚至比传统的几个排序:快速排序、归并排序还要简单、好写的多。 堆这种数据结构,目的是实现一种优先队列。它可以在比较好的复杂度下实现优先队列的各项操作。 二叉堆的实现,典型的使用的是线性存储,这一点和线段树比较像(原谅我这几天写了太多的线段树,脑子都写迷糊了)。 构建方式采用的是上浮法。通过让每一个节点与父节点比较,如果节点比他的父节点更加优先,就将其递归的上浮,直到根节点。这样可以对每一个结构进行遍历。 从此处我们可以看出,需要注意的是,二叉堆并不是一颗搜索树。它只能保证顶端节点是最优先的节点,而不能对其他任何节点给出判断。因此二叉堆是不能用于静态查询的。通常的静态应用会采用二叉堆进行一次排序。 给出一段上浮的示例代码:(和线段树一样,这里通常采用一个1-base的数组) 1 2 3 4 5 6 7 8 9 void siftup(int node) { if (node == 1 || node == 0) return; if (storage[node] < storage[node >> 1]) { swap(storage[node], storage[node >> 1]); siftup(nodec >> 1); } return; } 堆可以比较高效率的实现优先队列的入队与出队。 对于一个入队过程,只需要将元素放到最后一位,然后对其执行上浮即可。这一点和构建一个堆是等价的。 对于一个出队过程,显然,我们已经知道出队的元素在堆的顶端。我们抽走队列的头节点,然后把队列的尾部放到头节点的位置。此后我们执行一个下沉的操作。对一个节点的两个子节点,对各个子树递归的下沉。需要注意的是,两颗子树都要进行下沉的判断工作,因为如前文所述,堆只有头部是满足最优先,而对其他的数据给不出任何判断。 给出一段下沉的示例代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 void heapify(int node, int treesize) { if (node >= treesize) return; if (node << 1 <= treesize && storage[node] > storage[node << 1]) { swap(storage[node], storage[node << 1]); heapify(node << 1, treesize); } if ((node << 1 | 1) <= treesize && storage[node] > storage[node << 1 | 1]) { swap(storage[node], storage[node << 1 | 1]); heapify(node << 1 | 1, treesize); } return; } 到此为止,这个过程非常简单。甚至来讲,我们把“把队列的尾部放到头节点的位置”换成“把队列的尾部和头节点交换”,我们就实现了一个原地的堆排序。这个排序方向和堆的优先方向是相反的。

2020/4/7
articleCard.readMore

懒政

郑州发放4亿消费券。 郑州市商务局相关负责人告诉记者,此次消费券发放分为红利性消费券和社会消费券两类。红利性消费券由相关部门对低保、低收入、特困、优抚等四类救助对象发放,每人500元;社会消费券3.2亿元,其中普惠型消费券和百货消费券、餐饮消费券、汽车消费券由市商务局牵头发放。 知道当初俄罗斯发私有化券的结果吗? 1992年月,俄罗斯国会通过证券私有化方案,规定把企业大部分股份出售给企业的职工和管理者,一小部分出售给外部投资者。俄罗斯企业私有化将前74年积累的国有资产经过估价,按1.49亿人口,每人一万卢布,无偿转赠给每个公民,每人只象征性支付25卢布,即可领到一张面值一万卢布的私有化券。 ...俄罗斯实行私有化以后,1992年当年通胀率即达到2501%,1993年844%,1994年214%,1995年131.4%,1996年 21.8%,1997年11%,1998年84.4%,1999年36.5%。导致81%的居民多年辛苦劳动的积蓄被彻底洗劫一空。广大民众遭到空前的浩劫。 前几天又有新闻 界首市人民医院服务承诺。 ...护士输液穿刺一次成功,不成功导致患者不满意,退还当日注射费。静脉穿刺抽血一次成功,不成功导致患者不满意,退还本次采血费。 ...门诊医生首诊诊查患者时间不少于5分钟,病区值班医生首诊诊查患者时间不少于20分钟,急诊病人5分钟内得到医生的救治,医生对住院患者每天查房不少于2次,否则退还当日诊查费。 经历了这次新冠肺炎,也看到了那些自以为是、得过且过的懒政官员的下场,有些决策者还是不能见不贤而内自省。 我们说“四个自信”,又不是“四个自大”、“四个凡是”。理论自信是建立在充分理解理论的基础上的。知道理论可以用来指导什么领域,从而坚定坚决地执行;而哪些又不在理论的框架内,需要灵活处理。 然而我们的某些决策者,本身是无能的,又不愿离开整个官僚体系,只能抄袭照搬别的决策者的成果。今天房管局提出惠民满意服务,明天医院就跑来抄一个。 这不是纪委所谈的不作为的懒政。这正是新的问责制度不完善,原来的懒政官员应付检查而发明的新懒政。

2020/4/2
articleCard.readMore

Placement New

前几天写vector,可把我难倒了。最开始是一个测试用例,没有默认的构造函数,这个时候应该怎样去处理内存的分配。 我想到,既然一个元素的大小,即sizeof()是固定的,我们就可以先申请这个空间,等到需要使用的时候再赋值。 memcpy() 失败 虽然只学过C++化的简化版C语言,但我想到了malloc()方法,这就是一个只申请空间,不去填充空间的方法。 这对于传统的数据,就是说所谓的POD(Plain Old Data)是很好的。我的整个实现逻辑也是memcpy(),这样的处理速度快的多。 但是不是POD的类,占用了堆来存储部分内容,在他的存储空间里只存储了指向堆的指针。所以memcpy()只执行了浅拷贝,拷贝的实际上是指针,它指向的堆内存储是不变的。当原对象析构释放堆的时候,拷贝对象就被破坏了;被覆盖的对象没有进行析构而是直接被覆盖,内部申请的堆内存就被泄漏。 同样,当被覆盖的地方原本不存在对象,即只分配了内存却没有初始化内存空间时,如果直接调用类的赋值,这个赋值方法是不能检测原来的对象是否正确的。一旦有针对原指向堆内存的指针,现在是随机指针进行的操作,例如直接向动态数组发起memcpy(),就会出现段错误,或者内存的错误覆盖。 所以,凯撒的归凯撒,经过查阅资料,我找到了下面两个C++的处理方法。 placement new 成功 为什么我最开始不用new呢?因为new在调用的时候会自动调用构造函数,而一个用例没有默认构造函数,new就会出错。 怎么解决呢?经过资料的学习,我找到了placement new这个用法。实际上,new和delete是通过以下的过程实现的。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 A* p=new A;B* t=new B[_NUM]; delete p;delete [] t; //<==>A* p=new A;B* t=new B[_NUM]; //the first step. This is equal to malloc(size_t). operator new(sizeof(A));operator new[](_NUM*sizeof(B)); //This is called 'placement new', //to exec the constructor on the p location. new(p) A();for(size_t in=0;in<_NUM;in++) new(t+in*sizeof(B)) B(); //<==>delete p;delete [] t; //exec destroyer explicitly. p->~A();for(size_t in=0;in<_NUM;in++) (t+in*sizeof(T))->~B(); //delete operator delete p;operator delete [] t; 过程中的placement new实际上就是按需调用构造函数,而operator new/delete则是只申请内存而不初始化,和malloc/free功能是一致的。好像构造函数是不能够显式调用的,所以placement new来解决这个问题。

2020/4/1
articleCard.readMore

Flipped: A Love Story

Flipped[1], written by Wendelin Van Draanen, tells a simple love story, with two protagonists, two families, two aspects and two sycamore trees. The story itself is no more simpler than any other lovestories. But the key points to set this naive and pure tale higher than those epic and magnificent love solemns ,and tear-harvesting tragedy with dilemma between love and death, might be it's Montage writing style and unique movements through the story. Through the traditional writing aspect, the love story between two is better delivered through a third person. This requires ingenius emotion revealing method, which is a core part in lovestory, to project, but not point out directly, the characters' mood onto their actions, the background, and designed plots. That's why most of these literatures are in the drama form, with monologue to express their feelings, without making receivers feeling the content unnatural. This book uses an extreme and hidden monologues inside each chapter, as the same way as Montage in the film industry, to perform as a Duet, while minimizing the extra performing stage needed to form this story inside the readers' mind. This creates a novel and unique reading experience, increases readers' tolerance for a relatively slower progress, to break the stereotype and fill protagonists with soul. The Montage is the surface. To maximize the Montage power, the book chooses characteristic-based movements, to common plot-based movements to link these parts. While using different scenes to discribe one same story, normal plot-based movements will push the contents into a repeat. The cooperated scene is observed almost the same, and no one likes to read a whole part of reorganized words. To ensure the specific aspect, the article should be served as 'main plot->separated plot->main plot' pattern, which weakens the original plot, and this mended-everywhere plot is unnatural. This book does not set the movement on plot. To the contrary, many core substories are just shrinked than others. The contents are set upon feelings about themselves, the other, and their families. We can see Juli growing from a naive and passion-drived young girl to a rational, with a mature understanding of the inner and outer beauty; and Bryce from a cowardly boy, who could only feel guilty but nothing after following others, to a self-guided independent person. Their love status may flipped between words, but their characters grow steadily and slowly. My love experience is barely empty, and I may have some natural reluctant to those epic lovestories. Compared to those love or live dilemma, I prefer a sci-fi one. But this lovestory is more like a growth diary. The love between Turandot and Calaf, between Jack and Rose, between Romeo and Juliet are attractive, but maybe the plain story between Juli and Bryce can resonate with us more. Chet in the article said, 'Some of us get dipped in flat, some in satin, some in gloss.... But every once in a while you find someone who's iridescent, and when you do, nothing will ever compare.' This story might not be iridescent, but can be a hard one to compare with. 本文是作为英语作业的一篇读书笔记,最早发布在这个网站。 ↩︎

2020/3/28
articleCard.readMore

我宁可呆在这样的疯人院里

这是一篇关于电影《飞越疯人院》的影评[1]。 我向来认为,不论自由与否,他人即是地狱。这部片子被赋予了太多的象征意义。总感觉一旦有什么片子描述什么追求自由,总会变成所谓的政治正确、集体高潮片,一群人在那里阴阳怪气,指桑骂槐。我倒是没看出太多的引申意味。 在我心目中,护士的形象反而比较正面。护士是卫道者,是秩序的维护者,她的行径都是可以预测的,是理性的。相较于社会的混乱、无序,疯人院反而是相当稳定的一个子社会。最开始尽管生活也不愉快,但始终是在护士的压制下的。而那个想抽烟的切斯维克要烟的歇斯底里,反而是墨菲开了个坏头,才演变得越来越不受控制。疯人院中所有的社会规则都是明示的,直到墨菲闯进来为止。 墨菲可能为疯人院带来了短暂的阳光。但这些疯人真的需要这样的阳光吗?从能动的疯人们用引食的导管给动不了的疯人喂酒,最后全部喝得烂醉如泥。我起初还为墨菲带来的活力与生气而感到高兴,但是墨菲也不是什么英明角色,只是个普通混混而已。从这里开始,到小伙子的死结束,整个剧情转向了混乱与失控。 而结尾墨菲被切除额叶,酋长闷死墨菲后出逃,只能说是交代了一个故事的结局。酋长成功飞越了疯人院,疯人院也重新恢复了秩序。一切又回到了从前。 不自由毋宁死。渴求自由的墨菲和比比特都得到了死亡。我无意讨论意识形态问题,但是精神病院是精神病院,是一个医疗机构,目的是精神疾病的治疗与收容保护,而不是一个大家都来追求自由的厅堂。如果真如评论中有人所期盼的那样的集体出逃,反而是对社会自由的极大破坏。人也有免受恐惧的自由。有人说这些不是精神病人所以应该放出来。我认为这个界定不是一个电影观众就能说了算的,而是社会与医学技术人员应该解决的问题。在上个世纪同性恋更多被主流医学界认为是一种疾病,也有因此接受额叶切除术的所谓患者。但就算现在早已否定了这样的观点,也轮不到你事后诸葛亮去批判一番。 我从来不觉得社会交往是很轻松和自然的事,尤其是现在这样一个过于复杂,多种价值体系共存,不同年龄的人之间存在不可磨灭的代沟的社会中,与其让我面对那些未知而无常的社会约束,我更愿意生活在简单而明确的规则下。或许我在疯人院里待了太久,已经成了病人了吧。 这篇文章后来发布在豆瓣上。 ↩︎

2020/3/24
articleCard.readMore

谈新发本地病例

这是我过去在知乎的回答。 现在看上去我们的疫情防控阻击战已经快要打赢了,但是敌人在暗我在明的实际改变了吗?敌人的反扑被我们彻底压制了吗?我们已经取得了全面、压倒性的胜利了吗? 很遗憾,都没有。 阻击战结束了,我们还有一场堡垒战。敌人是不会打瞌睡的。现在敌人已经暂时退却了,但这只是一种战略上的迂回。敌人要搞包围,我们的战力却不足以突破他。 怎么办?我们现在暂时肃清了这座堡垒,最简单粗暴的来看,只要堡垒的门一关上,敌人就真的拿我们没办法。 但我们同时向敌人和朋友关闭了大门。好在敌人会露出马脚。我们只需要将这些希望进来的人关在瓮城里,就能够将敌人找出来,那时候在把朋友请进来。 胜败乃兵家常事。敌进我退,敌退我追,是军事斗争中总结出的经过实践检验的方针。打了败仗,需要撤退,是战争的动态变化。只有玩忽职守,瞒报军情,才会被军法处置。 不知道从什么时候,我们开始讲常胜之师,开始讲从胜利走向胜利,这样的宣传不符合客观规律, 也不利于正面的持久作战的决心。当没有办法从胜利走向胜利之时,就会自我否定,士气受挫,从而一次失利次次失利。 尤其是在阻击战进入决胜阶段之时,全国的数字前几天甚至都已经下降到0。如果不是强制的疫情上报规定,这些决策者甚至不会继续向上汇报,而重蹈最早的瞒报漏报的覆辙。这么多年的理论阵地,已经让他们产生了“别人都没有继续增加了,我们再增加就是工作不力,破坏了经济向好的形势”的感觉。 甚至不再进行隔离,也不戴口罩,连最简单粗暴的封闭口岸也不去做。他们担心自己被问责。本身经济任务就难实现,现在已经在复工,还要让经济重新停摆一段时间,以前都没有封闭入境口岸,现在要封闭,势必影响经济。可能稳定大过一切已经让他们引申出“死两个人不成气候我明天照样上班,万一完不成今年脱贫攻坚收官的目标我就秦城终老”的无知想法。 现在已经有关联病例,说明新一轮的传染已经开始。而这种只允许胜利,不允许失败的思想走向,对于打赢这场总体战是非常有害的。

2020/3/23
articleCard.readMore

勇敢与智慧

这篇文章是《古希腊文明演绎》课后的感想[1]。 “我们爱阿喀琉斯的同时,却挽着奥德修斯的手前行。”这句话放诸四海而皆准。在勇敢和智慧上,虽然这里我们将其并列,但是人们往往不会将这两者放在一个等同的位置上。我们往往认为,智慧是上天、是神赐予的,并且相信智慧只能通过经历的积淀而积累,但勇气往往只是一瞬间的事,仿佛一咬牙,勇气就自然而然地从牙根里冒出来。我们往往将自身的愚钝怪之于父母,怪之于命运,而将懦弱归结为意志力的不坚定。 我所理解的勇敢,或许也是希腊人所爱的那位阿喀琉斯,并不是那种屠夫结束了上千生命之后产生的那一种麻木,凭借不断试胆就可以逐渐掌握的一种精神窍门。而是一种对自身能力的认知与信任,而这种能力更加依赖经验和天赋。因为曾经浸在冥河中,阿喀琉斯对于自身的能力有着绝对的信任,因而在战场上可以向死而生,所向披靡,但也因对自身弱点的认知不足,丧命于足跟上的区区一箭。诸葛亮是勇敢的。他不畏数倍于己的敌军,大唱空城计,也是来源于对司马懿心理的十足把握。 对于阿喀琉斯我们只能报以爱慕,因为真正能够同行的也只有智慧。智慧可以共享,共享的过程中还会放大智慧的效益。三个臭皮匠,顶个诸葛亮,智慧在合作时得到升华。但勇敢只能是一种个人的私有物。勇敢者可以成为很好的领袖与旗手,但是一山不能容下二虎。如果没有奥德修斯,阿喀琉斯的盔甲要由紧随其后的勇士们去选出一个来继承,无论是谁,这些勇士的勇敢将不再存在。曾经可以将后背相托付的同伴变成了明争暗算的对手,丧失了对于自身能力的信任,勇气也随之消散了。 有些时候,我们自身展现出的勇敢,只是突然产生的情感的产物。这样的勇敢是建立在盲目和煽动上,往往变成了俄罗斯轮盘赌。人是脆弱的,是磨难的,我们没有神那样的资本去试错,在情感和理智的边界上,偏向哪一边都是致命的。勇敢需要从情感和理智的共同作用下产生,只有这样的勇敢,才不至于以卵击石,也不会虚度光阴。 本文最早写作于3月17日,是该课程的一次作业。 ↩︎

2020/3/18
articleCard.readMore

Powerful but Limited

这篇文章是《古希腊文明演绎》课后的感想[1]。 人天生就存在着悲剧性。作为意识与思考的载体,人的躯壳并不能很好的实现保护他的容纳物免遭破坏。相较于野猪、老虎这样的生存机器,不仅有着极其强大的生存和免疫能力,让它们能够在一片山林中独当一面,配套的神经也恰到好处,没有过多的累赘消耗本就稀少的生存物资(人的大脑往往消耗人日常摄入能量的三分之一)。这些生命在健壮性上走到了极致。 而人类显然不是一个这样一个设计精密,万无一失的机器。自人类出现的六千万年以来,从树上生态的参与者,到草原生态的掠夺者,到种植作物的生产者,再到现在铅与火,光与电的操纵者。人不是一个完美的产物,而是一种能够不断地向前发展与更新的迭代。 不完美造就了人的悲剧性。“人是一颗会思想的芦草。”帕斯卡如是说。与达尔文的生存理论不同。我们不是为了生存而生存的。Powerful but limited.甚至仅在可以观测的历史里,人的改造世界的能力展现出了巨大的进步,从生态位的中下层一举跃出动物间的食物金字塔;但是从出土的早期人类化石来看,人本就弱于平均的生存能力相较于早期人类与类人猿,甚至有所下降。霍金这样的科学家,更是将生存的有限和思维的强大两个角度的撕裂展现得淋漓。 古希腊人心目中的神,一个核心特性就是不朽。单从追求不死这一点来看,古今中外都是人永恒的幻想。人的大脑有着长达120年的细胞寿命,但躯干甚至维持不到它的2/3。人生是短暂的。相较于道家对于羽化成仙的追求与武士道对生命的极端漠视,又或是三大宗教对未来的幸福的画饼式憧憬,古希腊人更多将目光投到“现世”上。从而踏踏实实接受人的命运,在此基础上从自己出发,把自己的能力展现出来。 古希腊的神不像其他地区起源的精神依托式的神那样是永远伟大光荣不朽的,他继承了早期原始信仰的黑暗与混沌,又得以在其上加以秩序的发展。这些神也有缺点,也会残暴,但却与人有着不可跨越的界限。对神的向往,与之对应的对美德的崇尚直接反映在人的行动上。他们致力于把自己的悲剧写得精彩,而不是要将每一个故事都强行圆上一个圆满的大结局。 鲁迅说“悲剧将人生的有价值的东西毁灭给人看”,关键在于有价值的人生,而非这个毁灭的过程。俄狄浦斯王所想展现的,不是杀父娶母的俄狄浦斯罪大恶极,得了多大的报应,而是俄狄浦斯以人的力量去反抗神的灾厄,却仍然难逃神示的宿命。这样的悲剧,limited的部分不再是短板,而成了人之所以为人,就像悲剧因为毁灭而称为悲剧,的重要部分了。 本文最早写作于3月16日。 ↩︎

2020/3/18
articleCard.readMore

Drafted

These days are wavic. From Coronavirus, to Xiao Zhan, to Dow Jones' fusing, to a propably worldwide financial crisis. While it's nearly a month from when I wrote the last criticize Small Talk. I do have some words occuping my mind. I'm angry to the worsing situation, domestic and global. I'm sad about my invest loss, and I have friends who's crude investment just shrinked by half and more. I'm worried that coronavirus is spreading, with the more damaging fear. This blog generator has a Draft function. And I was trying to make a sound. I tried normal Small Talk, I tried sacrasm, I even have a halfway-finished short fiction. But I can't put them into my blog. These words are unorganized. They are topicless, just aimed at some surface and never hit the core. I have no specific one to blame. Of course I can blame it to Coronavirus, to Saudi's crude policy, to Capital manipulated public opinions, or we can just make this Destiny's fault. We can use such scientifically-prooved analysis methods to calculate out another branch to be cut, and saying that's the chaos' root. Or even we can use the always-correct Marx's class analysis, and point out It's a Capitalism fate, or a Humanity loophole, or both of them. These can solve nothing. Blaming and fault-discovering can solve nothing. We know we are experiencing the Sisyphus' stone, the unpreventable and looping financial crisis. Marx's theory is even worse. He just sentenced the certainity, cut off the merely-existing hope, and educated the upper class to transfer contradiction into race, ethnic, nation, and even climate. Exploitee are still using 19th-century methods, as strike, gatherings, demostration, while capitals have their gunners, trumpeters, and spies upgraded to 21-century informationized. Proletariats are less educated. They are taught to be familiar to work tools, but not an independent and creative thought. Marx pointed out a finish line, but not path to it. While exploitees are sunken in alcohol, marijuana, and entertainment contents generated just like by some automata, poisoning and filling their head with meaningless content, specialists have already memorized the Capital or Nation and Revolution, and designing surgical conterstrike on it. My blog's Small-Talk should be out of politics for some time. Carpe Diem and live a shortsighted life.

2020/3/17
articleCard.readMore

VLC Libva Error Troubleshooting

VLC player is well-designed and powerful, and I prefer this type of software designing. (Although UNIX's politically correct answer is Keep It Simple, Stupid, In the multimedia region I prefer a powerful and what-you-see-is-what-you-get interface, just the same design of those old cassette recorder.) VLC is preinstalled with KDE, or some basic structures. I couldn't play video, but audio is OK since then. Problem Description Everytime I tried to play a video, e.g. MP4 with VLC, the interface just flashed and nothing displayed. using journalctl I got 1 kernel: vlc[33836]: segfault at 168 ip 00007fb96213e11c sp 00007fb920207320 error 4 in libva.so.2.600.0[7fb96212d000+13000] and I tried vlc -vvv to show the verbose messages, I got 1 2 3 4 5 6 7 8 9 10 11 [00007f82180017f0] egl_x11 gl debug: EGL version 1.4 by Mesa Project [00007f82180017f0] egl_x11 gl debug: extensions: EGL_ANDROID_blob_cache EGL_ANDROID_native_fence_sync EGL_CHROMIUM_sync_control EGL_EXT_buffer_age EGL_EXT_create_context_robustness EGL_EXT_image_dma_buf_import EGL_EXT_image_dma_buf_import_modifiers EGL_IMG_context_priority EGL_KHR_config_attribs EGL_KHR_create_context EGL_KHR_create_context_no_error EGL_KHR_fence_sync EGL_KHR_get_all_proc_addresses EGL_KHR_gl_colorspace EGL_KHR_gl_renderbuffer_image EGL_KHR_gl_texture_2D_image EGL_KHR_gl_texture_3D_image EGL_KHR_gl_texture_cubemap_image EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap EGL_KHR_no_config_context EGL_KHR_reusable_sync EGL_KHR_surfaceless_context EGL_EXT_pixel_format_float EGL_KHR_wait_sync EGL_MESA_configless_context EGL_MESA_drm_image EGL_MESA_image_dma_buf_export EGL_MESA_query_driver EGL_NOK_texture_from_pixmap EGL_WL_bind_wayland_display [00007f82180017f0] main gl debug: using opengl module "egl_x11" [00007f8218658f00] main generic debug: looking for glconv module matching "any": 4 candidates [00007f82180017f0] glconv_vaapi_x11 gl error: vaInitialize: unknown libva error [00007f82180017f0] glconv_vaapi_drm gl error: vaInitialize: unknown libva error [00007f82180017f0] glconv_vaapi_drm gl error: vaInitialize: unknown libva error [00007f8218658f00] main generic debug: using glconv module "glconv_vaapi_drm" [00007f82180013b0] main vout display debug: using vout display module "gl" [1] 32678 segmentation fault (core dumped) vlc -vvv Way to Solution microcode problem? Though the Archwiki page VLC has some suggestions on segmentation fault, it indicates it as a microcode problem. That's something happens as missing VLC interface. I can play audio file with VLC, after trying the solution I think it's not the microcode problem. VLC bug? Some reports says it's a bug, that older VLC uses strlen() on a null pointer, but they were posted years ago and I believe it's not my problem. My VLC is fully updated. libva error: Display drivers When checking vaapi and libva error, I found it might be a problem with NVIDIA GPU. Some articles point out that Intel uses vaapi and Nvidia uses vaapu. After updated nvidia, libva, the problem still happens. Checking VLC's optional dependencies, I found 1 2 libva-vdpau-driver: vdpau backend nvidia libva-intel-driver: video backend intel not installed. It could be essential if you have these GPUs. After installing these two (my laptop uses both intel and Nvidia to display) drivers, my problem finally solved.

2020/3/3
articleCard.readMore

Ring-Fit新上手

趁着疫情时期宅在家中,新购买了健身环大冒险。 以前就见过体感游戏,研究过Kinect,也用Wii打过网球。体感很多都是一个简单参与性质的,但是没有想到竟然有以体育运动作为核心的体育游戏,甚至还有专门的体感外设。 我已经连续好几周没有活动过了。在学校里面本来也很少活动,这次疫情发生,我活动的时间就更少了(还更加理直气壮了)。这次用ringfit算是这几周来第一次主动去运动。 我在买ringfit之前,看到网上的评论有比较多的负面观点。有知乎上的健身大牛直接评价“心率起不来,连基本的运动效果都得不到满足。”但是我拿起健身环之后,还是感觉效果非常不错。尽管我调的运动强度算是非常低的数值,但是整个运动对于我来说也是中到高强度的内容。(这个评价也是因人而异,并不是说负面评价都是编造的,对于我这种平时就很少锻炼的人来讲还是比较合适。) 前面几个关卡,虽然说是简单的教学关,但对我来说还是有挑战性。我的基础代谢比较高,但是心肺功能又没有因此而带上来,导致长时间运动非常吃力,耐力很不足。最后的小boss真的是做到难以继续下去。感觉游戏自身的设定还是很好的。 从游戏说到游戏。貌似柏拉图对于游戏的评价是幼体——动物的幼崽与人类幼儿对于实际的模仿,亚里士多德提出是一种无目的的休闲形式,在今天这一点已经很不同了。尤其是生产力水平大幅提高,给了成年人充足的休息与娱乐的时间,娱乐手段也极端地多样化之后。ringfit这种内容可能还不能算上是大众的游戏,但是现在的游戏也越来越脱离现在被称为幼儿游戏的“模仿实际”,和简单无目的的休闲形式,有了新的内涵。 游戏与动画产业一样,都将曾经的“儿童”属性剥离出来,增加了自己内容的可选择面。现在的游戏受众向青少年,更多的是青年转变,也不在拘泥于幼儿也可以懂的简单叙事,而是向内容取材广度与内涵深度拓展。游戏也在向更丰富的社会功能发展,比如叙事艺术,比如思想与甚至理论的教育,再比如我们今天谈到的健身。 虽然不可避免的,当今社会的单一价值观总会将娱乐,和他的纯粹代表游戏放在道德的对立面,但是社会压力普遍增大,上升空间缩小,生活成本增加的情况下,游戏的市场获得了较大的提升,而游戏的内容也极大地丰富拓展了。不仅是逃避社会现实的工具,而更多起到了价值观丰富化的作用。文化的发展很多时候需要这一点,而单一价值观的社会总会面临多元文化衰退的危险。 我不想将问题引到体制和精神上,但是现在公开与奋斗论唱反调的人越来越多,娱乐事业的快速发展也在为这样的思想暗加砝码。缺乏前驱力的社会固然是我们不想看到的,但是马车夫的鞭子是否抽的过重了?劳动本身是快乐的,这种快乐也是高层次的、纯粹的。虽然不用鞭子和缰绳,会让“一群厨师做坏一锅汤”,但社会仍然需要的是多元价值体系的共存,而不是把其他的价值体系都一路归于钱和名利(这两者,在一定程度上是等价的)上。 我们甚至有微积分和运动的游戏。学习也是带来快乐的,运动也是带来快乐的。为什么我们要刻意将快乐与我们的道路分开呢?我们研究了两千年的教育制度,却旨在剥夺那些不是第一名的多数学生仅存的些许快乐。运动被量化在秒表,米尺和积分板上。马渴了自然会喝水,强迫也无济于事,对人这样做,还会有反作用。为什么我们要如此处心积虑地去逼着马儿喝水呢? 人有惰性,并且喜新厌旧,这是事实。但是游戏也是人在玩,很少有人指出来,游戏厂商反而还用这个赚了大笔钞票。有些时候我们只需要对事,而没有必要让每个人都成为圣人。我们需要利用人性的弱点,而不是试图用各种威严来弥补它。

2020/2/28
articleCard.readMore

政治艺术化与艺术政治化

这是一篇关于电影《意志的胜利》的影评[1]。 政治理论离不开宣传。人们只有在接触而不产生反感的前提下才会有动机去继续深入了解。二十世纪社会主义剧变,追其原因,共产社会美好未来受到普遍否定、西方式民主自由的鼓吹、社会主义国家之间的内讧,使剧变国家的政治话语权被架空。人民对执政者的理念产生了拒绝、反感,认知和践行就沦落为作业、任务,最后不免世俗化。 国社党虽然早已不存在,但国社理论在现在的逆国际化风潮下,煽动力依然不可小觑。国社主义和共产主义都充满着革命浪漫,对于它们的目标受众来讲是很难用他们自己的理性去客观认识的。政治宣传的感性、主观表达就成为了其中的关键部分。 艺术是人类主观意识的集聚,被政治所利用也是理所当然的。透过政治需求提供的近乎无限的资金与人力,而艺术不会受到任何损失。正如这部电影解禁一样,艺术需要政治化其本身,优秀的艺术作品又可以在一个恰当的时机与政治脱钩,凭借政治赋予的发展空间成为纯粹的艺术。人们站在大卫塑像面前,是不会想起撑起塑像的美迪奇家族的。 这部纪录片,干脆的没有任何旁白,没有任何剧情,只是将所有的镜头以时间顺序连在一起,就足以产生强大的感性表现力。高耸的长条旗帜,庞大的游行、旗帜队伍,冷峻、神圣的建筑,佐以狂热而兴奋的人民,快乐的青年们,电影塑造了国家社会主义本身理想与追求、又届已实现的胜利姿态。与其说是纪录片导演的技巧与手法,不如说是记录的事件本身对于观影个体产生的震撼与微妙的压迫感。 国社党在政治宣传上的能力绝对是世界一流水平,放到现在来看也毫不逊色。戈培尔博士领衔,文化传统厚重的德意志在这样的引导下,取得宣传技术与策略上的成功是必然的。将需要长时间思考、理解和认同的政治观念,转化为宏大而极端化的艺术作品,一下突入你的眼球,通过直接的图像并行化的影响你的认知。 国社作为一个上层建筑先于群众基础的运动,可以说是从楼顶向楼下盖房子的过程。政治的艺术化使这样的建设速成成为了可能,并具备了理论外输的能力。这一点苏联和德国的做法相似。相似的艺术作品,相似的权力展示、甚至受到了相似的攻击与评论。我国不具备这样规模化、体系化的文艺工作,艺术化的展现反而有误打误撞的意味。60年代的国内运动通过理论抽象神圣化、集体崇拜与同样宏大、夸张、极端的游行,同样可以以不自知而展现出的感性与疯狂吸引那些不明就里的日本、法国青年,尽管这样的理论比起前两者来讲更加的冲动与不成熟。 导演本身因为这部影片承受了很多不属于她的批评与指责。但人是相通的,在未来的propaganda下,谁又能立于潮流之外呢? 这篇文章写作于2019-09-28,最早发布在豆瓣上。 ↩︎

2020/2/25
articleCard.readMore

动态规划——从分割等和子集入手

引入 题目: 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200 (Cf. LeetCode,2020) 这道题目,在动态规划里,实际上是一个典型的01背包问题。但是我并不会动态规划,大佬们讲的方法,都是默认已经掌握了动态规划问题的处理方法来讲解。 这些解法对于我来说看得很痛苦。因此,我们通过这一道题来理解动态规划中01背包问题的解决原理。 1 解决 1.1 从枚举开始 最容易想到(甚至不用想就可以得到)的是枚举法。代码我们不再赘述。这样操作的时间复杂度是 $O(n2^n)$ 。主数组中的元素,要么包含、要么不包含在子数组中。枚举每一个可能的子数组,再对数组进行求和,就得到了我们的答案。 但是这样的复杂度是很难让人(和判断程序)接受的。它甚至比指数复杂度还要大。 我们观察这样的过程,找一找可以优化的步骤。我们发现,这样进行了大量的重复求和。从{1,2,3,4,5}到{1,2,3,4,5,6},我们可以用一些方法来保存{1,2,3,4,5}的加和,就可以节约这些时间。 1.2 分治法 这里的分治,实际上仍然是对于主数组的枚举,因此在枚举上的复杂度($O(2^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 //0.1 divide and conquer TLE class Solution { public: bool canPartition(vector<int>::iterator begin, vector<int>::iterator end, int sum) { if (begin == end) return false; if (*begin == sum) return true; if (*begin > sum) return false; return canPartition(begin + 1, end, sum - *begin) || canPartition(begin + 1, end, sum); } bool canPartition(vector<int>& nums) { auto it = nums.begin(); int sum = 0; for (; it < nums.end(); it++) sum += *it; //-------------------------------------------- if (sum & 1) return false;//这里是比较小的一步优化, //因为奇数和不可能被分割,就直接得到答案。 //-------------------------------------------- it = nums.begin(); auto end = nums.end(); return canPartition(it, end, sum >> 1); } }; 我们通过函数的调用栈来保存了这个临时的加和。这样整个过程的复杂度就降到$O(2^n)$。 但是这样复杂度仍然非常高。O(2^n)的复杂度,在实际的运用中都很难碰到。 我们继续观察这个过程,找一找可以优化的步骤。 我们观察到:对于问题的一个实例{1,2,3,2,8,7,9,6}: 进行到第三步:这样两个子数组 {1,2,不取}和{不取,不取,3}他们的加和都是3。这样造成了重复,他们接下来的比较实际上是相同的。对于后续的数列{2,8,7,9,6},3到底是哪些数字加出来的并不会影响。如果去掉这些分支,我们就可以节约这些时间。 1.3 基于二维数组的动态规划 动态规划是什么?Quora上有一个很有趣的例子: 什么是动态规划? *在纸上写:1+1+1+1+1+1+1+1=?* 这个式子等于多少? *数了一下*……8! *在式子的左边添一个“1+”*,现在呢? 等于9! 你怎么算的这么快? 因为你只是添加了一个! 所以你不需要重新加一遍,因为你记住了他以前有8个。“动态规划”只是一种“把东西记下来以节省时间”的洋盘说法。 我们可以看出,我们刚才使用基于递归的分治,就是一种隐性的“利用计算机的调用栈把数据记下来以节省时间”动态规划法,只是你不知道他的名字。 刚刚提到,分治的做法会重复的计算相同和的部分,因此我们现在这样做: 对于一个实例{1,2,3,5},我们建立如图的这样的一个二维数组表格: 这个表格中的数据是怎样得到的呢? 首先计算出目标和(就是分割的子集的和),建立对应的数组。 将第一行(也就是空集合对应的行)全部置false。再把空集和为0的(0,0)格子置true。 下一行 我们先将上一行为true的对应的和的格子置true。因为这些在新集合的真子集中存在,那么他们在新集合中一定存在。(文中用==>箭头表示。) 我们再将上一行为true对应的和加上新增的集合元素值所得到的和对应的格子置true。(文中用-->和圆圈[与0相加]表示)。 我们判断目标和的格子是否为true。如果true出现那么返回true。(文中用方框标记。)如果true不出现,我们进入下一行,重复这个过程。 全部遍历目标和格子仍然为false,我们返回false。 我们注意到,现在所需的时间就已经缩短到$O(N*sum)$.这样的时间复杂度远远好于前文所述的方法的时间复杂度。这样的一个示例代码给在下方: 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 class Solution { public: bool canPartition(vector<int>& nums) { auto it = nums.begin(); int sum = 0; for (; it < nums.end(); it++) sum += *it; if (sum & 1) return false; //计算目标和,建立数组。 bool** b = new bool*[nums.size() + 1]; for (int i = 0; i <= nums.size(); i++) { b[i] = new bool[sum / 2 + 1]; } //第一行的设置 b[0][0] = true; for (int j = 1; j <= sum / 2; j++) { b[0][j] = false; } //接下来的各行 for (int i = 1; i <= nums.size(); i++) { //将上一行为true的对应的和置true。 for (int j = 0; j <= sum / 2; j++) b[i][j] = b[i - 1][j]; //上一行为true对应的和加新增值置true for (int j = 0; j <= sum / 2; j++) if (b[i - 1][j] && j + nums[i - 1] <= sum / 2) b[i][j + nums[i - 1]] = true; //判断目标和 if (b[i][sum / 2]) return true; } //目标和格子仍然为false,我们返回false。 return false; } }; 这样的操作虽然耗时大大减少了,但是空间复杂度更大了。 我们再次观察这个程序,看看有没有可以节约空间的地方。显然,这个二维数组是空间消费的大头。我们注意到,每次我们都会把上面的数组结果复制下来。因此我们可以用一个一维数组来简化这个数组。 1.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 class Solution { public: bool canPartition(vector<int>& nums) { auto it = nums.begin(); int sum = 0; for (; it < nums.end(); it++) sum += *it; if (sum & 1) return false; //建立一个一维bool数组 bool* boolarray = new bool[sum + 1](); sum >>= 1; for (it = nums.begin(); it < nums.end(); it++) { //原本为true对应的和加新增值置true for (int j = sum; j >= 0; j--) if (boolarray[j]) boolarray[j + *it] = 1; //自己置true boolarray[*it] = 1; //判断目标和 if (boolarray[sum]) return true; } //没能达到目标和 return false; } }; 整个过程是相同的,但是内存的占用量减小了很多。因为内存动态分配需要消耗大量的时间,这个做法的耗时也比原来的耗时少一半。 当然,在程序堆里分配空间非常耗时,也可以从条件出发,直接将数组定义在程序栈里,可以更节约时间和空间。

2020/2/21
articleCard.readMore

被背叛的革命 (1)

这是《被背叛的革命》(列昂·托洛茨基著)前三章的摘录与书评。 取得了什么成就 如果你们记得,社会主义的任务就是要建立一个以团结一致和一切需要得到妥善的满足为基础的无阶级的社会,那么,从这个根本的意义上来说,苏联还没有一点社会主义的影子。 >>P V<< 社会主义已经表明,它有权取得胜利,不是在《资本论》的书页上,而是在包括地球表面六分之以的工业舞台上——不是在辩证法的语言中,而是在钢、水泥、和电力的语言中。……这个不可摧毁的实施,即一个落后国家只是由于进行了无产阶级革命就在不到十年的时间力取得了史无前例的成就,也仍然还是未来的实际景象。 >>P 3<< 国内价格和世界市场价格之间的差别,就是衡量这种力量对比的主要手段之一。然而,苏联统计学家甚至连接近这个问题都遭到禁止。原因是,尽管资本主义处于停滞和腐朽的条件下,但是在技术、组织以及劳动熟练程度方面,它还是远远走在前面的。 >>P 4<< 一种理想的计划管理,所要保证的不是个别部门达到最大限度的速度,而是整个经济最适当地发挥效能,从这个立场出发,统计出来的增长率在初期是比较低的,但是整个经济,特别是消费者,将会得到好处。归根结底,总的工业动力也会得到好处。 >>P 8<< 资本主义制度的根本弊病并不在于有产阶级的奢侈生活——尽管这一点本身也许是可恶的——而在于这一事实,即资产阶级为了保障其过奢侈生活的权利,保持了它的生产资料私有制,这样就是经济制度注定了限于无政府和衰退的状态。在消费奢侈品方面,资产阶级当然拥有垄断权。但是在基本需要品方面,劳苦群众是站压倒多数的消费者。 >>P 11<< 苏维埃政权正在经历一个准备阶段,它正在输入、模仿和吸取西方的计数和文化成就。生产和消费的相对系数证明,这个准备阶段还远远没有结束。甚至在未必会有的资本主义继续完全停滞的条件下,这个阶段也还是必须经历一整个历史时期。这是我们在进一步考察以前需要的第一个极其重要的结论。 >>P 12<< 苏联在当时取得一系列的经济成就,实际上与一战结束,世界经济萧条萎缩,资本主义本身自身难保,无暇镇压颠覆新兴政权有关。沙俄本身具备一定的资本与生产基础,这是其他社会主义国家难以复制的。 资本主义社会能够消灭封建社会,其根本原因在于生产力的解放。时至十九、二十世纪,资本主义已经发展到“可以保证封建不再复辟”的程度了。而社会主义经济,尤其是从生产资料公有化到满足一切社会需要,这两点之间应该寻找怎样的路径,实际上到今天也没能得到比较妥善的解决。 资本需要利用人本性上的贪婪和欲求来追求无限的生产扩大化和多样化。日新月异、繁复诱人的新产品、新发明,实际上是现代资本主义发展设下的一个发展循环,通过绕路来延缓到达经济危机的时间。这些物品本身就是资本主义赖以生存的核心。斯巴达式的共产主义就是乌托邦。只要人的智力和想象力不枯竭,从人最自然、不受到教育和约束的愿望出发,资本主义的生活方式一定远超斯巴达式的共产主义。 尼克松与赫鲁晓夫有过一场著名的厨房辩论。 尼克松: 这是我们最新型的洗碗机。这种型号生产成千上万,被直接安装在家里。在美国,我们喜欢让女性的生活更轻松…… 赫鲁晓夫:你们资本主义对女性的这种态度不会在共产主义社会发生。 但是赫鲁晓夫最终还是输了这场辩论。事实证明,苏联人民还是无法抵挡洗碗机、麦当劳十足的诱惑力。 俄国早期的共产主义者很有斯巴达精神,但是他们的孩子们不。尽管数据上苏联是,在五年计划甚至更早,就是世界上不可或缺的主要工业国,但是重工业优先的问题从那时开始,一直延续到解体。甚至今日俄罗斯仍然面临着轻工业薄弱的困境。 苏联的工业可以表述为这样一条独特的法则:越是接近群众消费的商品,通常总是越糟。 >>P 7<< 享乐在很早,就被斯巴达主义者们冠以了腐朽的标签。动摇这一点,甚至会影响苏联存在的合法性和社会主义的优越性。因此苏共的官员们不是不了解享乐——有些时候他们反而更了解这些——而是不能够公之于众。苏联的重工业越是发达,轻工业的生产者们就越是回避这一问题,这会让好看而美观的全联盟大会公报上留下一个擦不掉的污点。 中国注意到了这样的问题。但是也没有提出更加社会主义的解决方案,而是借用资本主义的方法来处理资本主义提出的问题,通过引入私人经济来解决变化不定的生活需求。早期的处理方式显得矫枉过正,但是这种处理是有效的。至少使用这种处理方法,没有像苏联那样解体。但这样做损害了斯巴达主义者和理想主义者们的热情,认为这样做“背叛了革命”。 中国也仍然在一个准备阶段,但是已经接近尾声了。不是说中国目前准备已经结束,而是世界资本并不像过去苏联的准备那样疲软,无力进行对抗。距离2008年经济危机已经过去了12年了。敌人是不会打瞌睡的。中国目前的理论合理性把太多砝码压在了经济增长上了。全世界经济下行、反全球化不断升级,互联网又将负面声音扩大化了,如何解决即将到来的理论危机,是一个难点。 经济增长和领导的左右摇摆 对于我们来说,似乎还有比要对苏维埃政府的经济政策及其左右摇摆的情况进行一次历史性的回顾,以摧毁那种人工培养出来的个人拜物教。这种拜物教认为,要取得成就——不管真的还是假的——关键在于领导的特殊品质,而不再与革命所床在的那种财产社会化的条件。 >>P 28-29<< 有一点当然还是不可理解的——至少以一种合理的态度对待历史的化——即一个思想最贫乏、犯错误最多的派别怎么会、为什么会反而占了上风,压倒了所有的其他集团,并且把一种毫无限制的权利集中在自己手里。 >>P 29<< 苏联的发展,从十月革命到斯大林经济体制的完全建立的这段过程,可以说是非常危险、而且不可以照搬的。社会主义经济的发展,一方面需要在全新的生产体制下调动经济本身的活动性,另一方面,需要在群强环伺的情况下,保卫经济安全。从军事共产主义到新经济政策,再到斯大林模式,这种经济政策截然相反的改变,在其他社会主义国家的建设过程中都是不可能如此快速地进行转换的。 托洛茨基的年代苏联还只是经济制度的变革,他没有看到的是,苏联每次政治变革,令人惊讶地没有造成太大的国内社会动荡,而真正的国内动荡,反倒是领导所愿意并主动推进的结果。苏联国家有着先天性的文化独立性,这是其他国家所不具备的。 社会主义和国家 无产阶级所需要的只是逐渐消亡的国家——即需要建立一个立刻开始消亡而且不能不消亡的国家。 >>国家与革命《列宁全集》B 22, P 390<< 同时无产阶级将采取措施来防止自己的及其转入官僚手中——“马克思和恩格斯详细分析过的办法:(一)不但实行选举制度,而且随时可以撤换,(二)薪金不得高于工人的工资,(三)立刻转到使所有的人都来执行监督和监察的职能,使所有的人暂时都变成‘官僚’,因而任何人都不能成为官僚。”你不要以为列宁所说的是十年当中的问题。不是。这是“我们应当而且必须在完成无产阶级革命方面开始“采取的第一个步骤。 >>P 35<< 因此,无产阶级专政制度从一开始就不再是原来意义的那种”国家“——即一个使大多数人服从的特殊工具。物质力量以及武器都直接立刻转入像苏维埃这样的工人组织手中。从无产阶级专政的第一天起,国家作为一个官僚工具开始消亡。之久是党纲发出的呼声——而且到现在还没有停止。奇怪的是,这种呼声听起来就像幽灵从陵墓中发出的声音。 >>P 35<< 法权永不能超过社会经济制度以及由此经济制度所决定的社会文化发展程度。 >>哥达纲领批判 《马克思恩格斯文选》(两卷集)第二卷 P 22<< 负有社会主义改造任务的国家,只要被迫用强制的方法维护不平等——即少数人在物质上的特权,那么它就依然是一个”资产阶级“国家。即使已经没有资产阶级。 >>P 37<< 如果国家没有消亡,反而变得越来越专横,如果工人阶级的全权代表们官僚化而官僚们驾于新社会之上,那么,这并不是由于过去的心里残余的次要原因造成的,而是由于只要国家不能保证真正平等就会产生并支持拥有特权的少数人这样一种铁的必然性所造成的结果。 ”牛奶是乳牛的产物,而不是社会主义的产物,你们实际上是把社会主义同一个乳流成河的国家形象混为一谈了,因此才不了解一个国家即使没有相当大地提高人民群众的物质条件,也能在一个时期达到较高的发展水平。“这几句话是在可怕的饥荒正在全国猖獗的时候写的。 马克思将社会主义界定为一种初级阶段的共产主义。这与我们对于社会主义的实际定义是不同的。如果要谈所谓的社会主义初级阶段(就是共产主义的初级阶段的初级阶段(笑)),实际上也不能够说是完成了社会主义。不论从形式上:工业是多种所有制共同发展,农业是以个体农业为基础的有限度合作,商业和服务业的绝大多数经济成分都是私有的;还是从理论上:没有完成生产力的高度发达,没有完成社会矛盾与需求的完全解决。我们都不能够自称为[一个马克思主义理论框架下的]社会主义。 很多人非常反对这一观点。他们指出,在中国特色社会主义理论体系的指导下,中国的经济、文化建设都取得了显著的进步。国民的物质生活品质和精神生活都比过去得到了显著的提高。现在中国的经济发展也处于世界领先的水平。 就如书中所说的,马克思认为共产主义的低级阶段是这样一个社会,它从一开始就比最先进的资本主义经济发展水平要高些。这个发展水平高不是指比10年前发展水平更高,也不是和19世纪的德国比发展水平更高。如果要这样比,甚至很难有国家达不到这一点。这个更高,是要和今天的美国比、今天的发达资本主义国家比,并且随着时间的推移这个水平还会水涨船高。 我们国家40年来改革开放取得了非常辉煌的建设成就。一举成为世界第二大经济体,这是非常值得称赞的。但是理论的建设却是在倒退。我们搞出几条红线,只要不压线,不管黑猫白猫,抓到老鼠就是好猫。仿佛社会主义只是一个名号,甚至连名号都不是了。 中国确实取得了辉煌的成就,但这样的建设过程,从联产承包到民营企业,形式上不是社会主义的,内核上更不是社会主义的。从资本的私有制度、剩余价值的剥削到仍然低下的生产力水平——这里的低下和上文所述的一样,不是与过去比,而是和今天使用资本主义方式进行生产的最先进的同类企业比——我们实际上,是在用革命家们拼搏出的公有资产赔礼,请回被革命家们赶走的资本家,好言好语求着先进的资本家们将我们建成一个先进的资本主义国家,并且妄想着等到成为真正的资本强国,拥有最先进的资本主义生产方式之后,再和蔼有礼貌地请资本家同志们放弃他们的财产,并宣布”我们建成了社会主义!“ 社会主义它先进的地方在于,通过生产资料的公有制,来减少不必要的低效能劳动,通过社会广泛的合作,来更好的利用资源和工具。而不是我们因为马克思教给我们社会主义先进,我们就先进。更不是给资本主义冠个帽子就据为己有。 我们离社会主义还有很远的距离,而现在的政治理论更是伯恩施坦化了。很明显的是,我们的官僚,如同资本主义国家的官僚一样,可以从这样的经济体系下得到更多的好处,但是社会主义完成了,他们的好处就结束了,他们要兑现过去将手按在马克思著作上而发出的誓言了。那么想必这样的经济体系持续的越久,得到的利益就越大。这样看来”我们处于并将长期处于社会主义初级阶段“可以说是一颗定心丸了。

2020/2/19
articleCard.readMore

Everything About DPT-RP1 When Using LINUX

Intro You can get official DPT-RP1 support while using Windows or MacOS. Sony has a well-designed software Digital Paper App, which supports everything in the user manual. Unfortunately, Sony do not support Linux. And the program above cannot be started by wine, as DPT-RP1 is using some weird mechanics: (setting the device as an USB-network adapter and controling the IO by posting and getting files from a virtual web server hosted in DPT-RP1.) and adding virtual printer and device drivers, which is unable to be handled by Linux and wine. Here we introduces a python command tool dpt-rp1-py to operate DPT-RP1 on Linux. A New Method If your device can connect to the same local network to your computer, you can just connect it and do as the original tool guide. With mDNS query installed on your computer correctly, you can create an alias like dptrp1="dptrp1 --addr digitalpaper.local" to speed up. Install and Config You can access the tool here. It supports pip3 so it can be installed easily by pip3 install dpt-rp1-py. However, in order to get the device work, you may need some technics which do not show in their homepage. Here is the extra step (it's also written in their docs). install the pyserial. (You can set it up by pip3 install pyserial.) Plug in your device and create a python script file(we assume the filename is 'connect.py') and add these lines to the file.(You can download the file here directly.) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/usr/bin/python #This is used for automatically setup DPT-RP1 #to usb-network, before python package dptrp1 #starts. #2020 Feb 18 by VICTRID #you should have dptrp1 and pyserial installed. import serial tty=serial.Serial('/dev/ttyACM0',115200) tty.write(b"\x01\x00\x00\x01\x00\x00\x00\x01\x00\x04") run the script with python. (You can type python ./connect.py or something like this. You can also edit the code if you know what you're doing.) De-activate DHCP on the new Ethernet device. If you're using DHCP to obtain addresses, you should disable it for the DPT-RP1, since the DPT-RP1 does not run a DHCP server. For example, if you're using Network Manager, change the IPv4 settings on the DPT-RP1 Ethernet device to 'Link-Local Only' instead of 'Automatic'. This will assign your end of the Ethernet link an IPv4 link-local address in the 169.254.0.0/16 range. When using Network Manager, also make sure that in the 'Ethernet' tab, 'device' is set to the interface name, not the MAC address. This will help Network Manager to restore the settings when connecting next time. Determining the address for DPT-RP1. You may use your DNS lookup to find the address of digitalpaper.local. e.g. you can run this: 1 2 3 4 5 6 7 $ avahi-resolve -n digitalpaper.local digitalpaper.localfe80::xxxx:xxxx:xxxx:xxxx (if the system returns deamon not activated, use your service controller to start it, e.g. # systemctl start avahi-daemon # service avahi-daemon start ) You need to get your device interface name. You may get it by ifconfig. It should be usb0 or enp0s20u1 or something. Then your full address should be something like [fe80::xxxx:xxxx:xxxx:xxxx%usb0], by adding the interface name after the address, separating by %. dptrp1 do not support searching your device under linux directly, so you should run dptrp1 with an additional parameter --addr="[fe80::xxxx:xxxx:xxxx:xxxx%usb0]" all the time (replace with your own address). You can add an alias like dptrp1USB="dptrp1 --addr=\"[fe80::xxxx:xxxx:xxxx:xxxx%usb0]\""to the shell to simplify, as the ip address won't change normally. Use the Tool Every time you plugged your device onto your computer, you need to run the file connect.py again to get your device responcing. You may get usage by directly typing dptrp1 -h. you should've got 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 usage: dptrp1 [-h] [--client-id CLIENT_ID] [--key KEY] [--addr ADDR] [--serial SERIAL] {command-help,copy-document,delete,delete-folder,document-info,download,list-documents,list-folders,move-document,new-folder,register,screenshot,sync,update-firmware,upload,wifi,wifi-add,wifi-del,wifi-disable,wifi-enable,wifi-list,wifi-scan} [command_args [command_args ...]] Remote control for Sony DPT-RP1 positional arguments: {command-help,copy-document,delete,delete-folder,document-info,download,list-documents,list-folders,move-document,new-folder,register,screenshot,sync,update-firmware,upload,wifi,wifi-add,wifi-del,wifi-disable,wifi-enable,wifi-list,wifi-scan} Command to run command_args Arguments for the command optional arguments: -h, --help show this help message and exit --client-id CLIENT_ID File containing the device's client id --key KEY File containing the device's private key --addr ADDR Hostname or IP address of the device. Disables auto discovery. --serial SERIAL Device serial number for auto discovery. Auto discovery only works for some minutes after the Digital Paper's Wi-Fi setting is switched on. For some command, you can get additional help by calling dptrp1 command-help <command>. If you're connecting to the DPT-RP1 for the first time, you may need to register the device. The dptrp1 cannot search for your device under linux automatically, so you must do it by dptrp1 --addr="[fe80::xxxx:xxxx:xxxx:xxxx%usb0]" register to register it. If your alias was set, you can just type dptrp1USB register. If you get an error, wait a few seconds and try again. Sometimes it takes two or three tries to work. Remember that automatic searching is not suitable for linux USB connection, so it's a must to add the --addr="[fe80::xxxx:xxxx:xxxx:xxxx%usb0]" parameter. Note that the root path for DPT-RP1 is Document/. Example command to download a document file.pdf from the root folder ("System Storage") of DPT-RP1: dptrp1 download Document/file.pdf ./file.pdf. Example command to upload a document file.pdf to a folder named Articles on DPT-RP1: dptrp1 upload ./file.pdf Document/Articles/file.pdf. Postscripts You may refer to the origin site for more advanced details, such as dptmount(experimental) to mount your device as a disk, or other interesting functions.

2020/2/18
articleCard.readMore

HTTP STATUS 451

文章说的很乱,辛苦读者您了。 这几天频繁地安装程序。Arch Linux的官方pacman源有国内tuna和sjtug镜像加速,速度很快,安装很省心。但是AUR就很痛苦了。很多AUR包提供的是Github源,而且aur.archlinux.org本身也不是很稳定,每次在终端打出yay都是痛苦万分的。那些没有pkgbuild的就更令人痛苦了。从Github上clone下来,看见1024字节/秒的速度,到了98%还要服务器断开链接。 npm也如此。虽然有国内源,但遇到自己打包的组合包,还动不动就带上electron,就看见下载也慢,build也慢,安了几个小时,报错,前功尽弃。 普遍在论坛上的意见是,Github本身的访问没有受到影响,但是Github使用的内容分发网络[CDN]都“不提供国内分发服务”。而曾经也有过Github本身无法访问的历史,但是很快就恢复了。 现在在Github上面开源自己的部分工具的国内互联网公司越来越多。很多repo都有百万行量级的代码,stars收割机。但是那些contributors真正在Github上面开发吗?一个人,9月14日一天,五百四十七万行代码。我又得知,很多企业甚至工作环境都无法联网。这些项目的issues寥寥无几,有pull request的项目更是凤毛麟角。而我打开vscode的repo,几百pull request,上千的open issues,还有上万的issues closed. 他们究竟是在为了开源而开源,为了stars而Github啊。 这么庞大的代码量,对应的却是比拨号连接还要慢的网速。 当然,我们都知道,这些媲美甚至超过国外顶尖同行,真正为国人争光的互联网公司,本身也运行着强大的内容分发网络。服务器载荷、资源分配、线路选择,背后的核心竞争力,都是无出其右的。 为了与国际接轨,他们也有更快的内部辅助服务器,为这些劳动结晶的发布与维护提供相匹的速度。甚至还有互联网服务提供商,租用他们的服务器,提供高带宽低延迟的数据转发而从中谋得利差。想必很多使用者对此都心照不宣,甚至已经将其作为互联网访问的常态。 而排除在外的那一部分人,残存的直连Github的小水管,应该只是用来标star的吧。 HTTP有一个状态451,以小说《华氏451》命名,内容和状态的解释不在此赘述。 当然,这样的做法此地无银三百两,更算是一种行为艺术。真正的Legal Reasons,绝不会像这样明摆出来。但是像Github这样的网站,难道不在返回“阳谋”的451吗?451代码是给浏览器看的。而这种阳谋的451,是摆给显示屏前的人看的。 不禁止你登录,但是给你使绊子。网站不是非法的,但ISP也不支持你访问。正如李鸿章招待外国使臣,给自己上晾凉的汤,而给外国人上滚烫的汤,再加上一层油,这样就不会冒热气。不敢明针对使臣,但是要给使臣在心里立上个下马威。 但决定权终究在李鸿章手里的。毕竟在清朝的土地上。 有什么办法呢?加速器赚得盆满钵满。只有在名单上的才能享受200的待遇。其他的要么是403/404,要么就是隐性的451。 不透明之处一定有利可图。加速器就是一个例证。谁也不知道Github还有几天的阳寿。 这不是好事:451终究会越来越少……

2020/2/15
articleCard.readMore

二分法——重复情形

假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 请找出其中最小的元素。注意数组中可能存在重复的元素。(Cf. LeetCode,2020) 这道题目被标记为困难。与题目基本一致但是被标记为中等的题目的区别在于数组中存在的重复元素。 这种题目类似于找零点,早在400年前,牛顿就已经给出了二分法的解决方案。而现在,我们的基本思路也是二分法,这可以让程序运行控制在 $O(lgN)$ 的时间复杂度里。但是这种方法存在一定的缺陷。在数学上,这样的二分法是失效的。比如这样一个数组: [3,3,1,3] 我们可以看到左边界、右边界和中点(不论是哪种取整意义上的,我们还可以加一个[3,1,3,3],没有任何的区别。)都是3.这样的数组,最小元素同时可能存在于左区间、右区间或者端点值里。这个时候,需要使用一些方法来在不变换的同时缩小区间。我们可以想到这个部分开始作枚举。 下面的代码使用了这一观点,但是通过巧妙的判断,将枚举的时间最小化。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution { public: int findMin(vector<int>& nums) { if(nums.empty())return 0; return findMin(nums.begin(), nums.end() - 1); } int findMin(vector<int>::iterator begin, vector<int>::iterator end) { // *begin>=*end,or not rotated. if (*begin < *end) return *begin; if(begin==end)return *begin; if(end-begin==1)return *end; auto mid = begin + (end - begin) / 2; if (*mid<*end)return findMin(begin,mid); else if(*mid>*end)return findMin(mid,end); else return findMin(begin,end-1); } }; 每次枚举后再进行判断,这样使得能够二分的时候都在二分,平均复杂度就更接近 $Θ(lgN)$ 一些。

2020/2/13
articleCard.readMore

鸽巢排序与桶排序

引入 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。(Cf. Leetcode,2020)。 有一种做法是这样的: 1 2 3 4 5 6 7 8 int* Count = new int[n](); for (int i = 0; i < n; i++) { Count[nums[i]]++; } for (int i = -; i < n; i++) { if (Count[i] >= 2) return i; } 这样的做法,我们同样可以用在排序中。对于上下界确定的欲排序数组,我们建立一个计数数组,遍历整个数组,进行计数,就可以直接输出对应的有序数组。我们称这种排序方法为鸽巢排序。这种排序方法在特殊的情况下可以实现时间复杂度 $O(n)$ 。 有些时候计数数组显得过于庞大,而且,例如对于一个双精度数组,就无法建立这样的对应数组。这个时候我们设立一系列“容量”相等的集合,将数组进行有序分割,再对被分割的子数组实施排序。这样的集合称为桶,这样的排序方法称为桶排序。 桶排序继承了鸽巢排序的优点,但是鸽巢排序占用空间大的问题仍然解决得不好。而且桶排序仍然有先决条件,可以说是一种特殊情况下的排序。 桶排序是一种分配排序。分配排序不需要进行关键码的比较,但需要知道待排序列的一些具体情况。(Cf.Jin-nuo,2016)

2020/2/13
articleCard.readMore

为什么要写博客

很多人的博客,基本上只有一篇“如何使用jekyll/hexo搭建博客”。顶多再来两三篇,就会放弃。 博客,在其他的领域里已经算是过时的一种媒体介质了。微博、twitter,人们已经不需要使用博客这种内容正式、形式繁杂的系统来完成他们的生活分享了。就算在仍然活跃的IT领域,(IT这个领域虽然技术变革比别的行业快的多,但是在某些方面还是很怀旧的。IRC(Internet Relay Chat)、telnet BBS论坛,也几乎只有IT领域的这些东西还在继续发挥余热,或者说,热度不减。) 网络上能看的博客越来越少。cnblogs,CSDN两大host充斥着整个search ranking(我不是说这两家上面没有优秀的博客和精彩的内容,但是劣币驱逐良币,绝大多数的博客都是个人的笔记本,对于前人的成果直接二话不说全文转载,再加上这些大的host本身内容就多,更容易获得高的search ranking,导致全网很多内容几乎都是重复的。),更多的内容创作者选择了更省心的创作平台,独立博客这种DIY的方式更是不被待见。 另一方面,很多人写博客的目的,很多时候也包含了希望别人看到、希望展示自己这一方面(其实我也有)。但是在现在的网络传播形式中,尤其是这样的新博客,往往只有作者一个读者。久而久之,往往很无趣,中途就放弃了。这甚至加剧了博客这种传播方式衰落的循环。 我的记性很差。只要不是一直在用的,基本上过了两三个月就会忘光。因此我认为写博客和记笔记写日记一样,都是记忆外部化的一个过程。同时书写的过程实际上也是一种对话,在问答里,思考能够得到具象化。哲学,据我的理解,也是一种具象化一个人的思想的思维训练。我的博客的目标读者实际上就是我自己。思想如果不付诸于实体,他就会像早上醒来的梦一样转瞬即逝。 因此我的博客里会有具体的学习内容,也会有闲谈和牢骚。有大佬提到说要想写一个成功的博客就不能光想着自己,要做你的目标读者想读到的内容。这个观点对于我来说是等价的。 我也是一个成果驱动型的人。而这个博客是我从git init命令开始一个字母一个字母地打出来的。(如果npm curl wget也算命令的话)有些时候光看看自己做的这个小widget,往往可以增强学习的动力。博客的好坏是全由我主观评价,而无关于他人的。而学习面对的困难与评价是客观的,我只能做到学习,分由别人打给我。做这些事情实际上是在短路我脑中的信心回路。 我不想半途而废地写博客,因为我过去做过太多半途而废的东西了(买域名的钱都已经掏了)。只要在写,我搭建这个博客的目的就达到了。 写作的目的是写作本身。[递归](笑) 本来新冠疫情情况就是如此,我呆在家里也没啥事干。 因此我不打算写如何用hexo搭建博客。搭建这个博客本身也是很简单的事情。另外又在使用hexo-admin插件,md的语法也没有什么难度,实在不行还可以用html写。另外,我如果不写这个“如何用hexo搭建博客”,是不是就不会最多两三篇就半途而废呢?(笑) 事情如果你真心想要做,自然就可以做好。

2020/2/13
articleCard.readMore

系统更换

引入 由于业务需要,我现在整体进行了操作系统的更换,经过几天的准备,现在基本完成。以下是重要的过程记录。 1. 大方向的改变 1.1 文件系统 从NTFS迁移到ext4。由于腾出了很大的空闲磁盘空间,因此直接拷贝,没有太带的问题。 本来打算安装双系统,结果因为windows自带的efi分区太小,我的电脑又是双硬盘uefi启动,整体的引导读取上有很大的问题,因此就干脆删除了整个windows分区。 1.2 操作系统 系统安装的是Arch Linux。因为Arch Linux的整体社区和wiki的氛围都比较好,个人又很喜欢KISS (Keep it simple, stupid)原则,也很认可Arch的实用主义观点,因此就使用了Arch。起初的安装过程(因为在调试之前的efi启动问题)并不顺利,但是后面就自然熟练了。(或许是simple了?w) 其实安装的过程中(就是uefi适配的时候)有过安装Debian的念头。我接触Debian系Linux比较早,也尝试过一段时间(图形化操作的Linux......),对于那些apt、dpkg的命令还是比较熟悉。现在有些时候还会弄混pacman和apt(也许是因为前两个字母相同?w)。结果Debian的图形化做的很好,但是还是没能解决efi的问题。因此还是重新安装了Arch Linux。 安装的过程应该不用赘述,但是还是要注意尤其是网络的安装。我在笔记本上使用无线网络安装,结果前几次连网都没有办法连上。至于网络的驱动问题,Arch并不会严格区分所谓自由不自由软件,整体是对症下药,比有一些还需要在程序源里设置non-free的设置好的多。 2. 系统与应用 之后安装了KDE的桌面环境。整体的过程都比较简单,毕竟所谓"ThinkPad是最适合安装Linux的笔记本"。 安装了Steam。这个应该是这次安装最超出我的预期的地方了。不仅很多的游戏都原生支持linux系统,而且steam对于移植的游戏的整体适配也很不错。 因为SDDM太simple(原文如此),因此为了配置更加丰富的登录界面,改用了GDM。 因为网络问题,更换了功能更加强大而可定制程度高的HEXO作为blog配置程序。你现在看到的这篇文章就是基于HEXO生成,在HEXO的插件HEXO-admin上书写的。 以下是其他已经完成配置的程序清单: wine Visual Studio code / LLVM chrome deepin-QQ/TIM LibreOffice 一款基于electron的网络加速软件 3. Have FUN Linux非常松散,尤其是Arch Linux,非常有可玩性。以下是我完成的配置设置 PAM fprintd 指纹登录 Google Authenticator 两步验证登录 [我在维护该中文wiki的翻译]

2020/2/12
articleCard.readMore

动窗法与前缀和——简单实践

题目是: SJTUOJ 1002.二哥种花生 二哥在自己的后花园里种了一些花生,也快到了收获的时候了。这片花生地是一个长度为L、宽度为W的矩形,每个单位面积上花生产量都是独立的。他想知道,对于某个指定的区域大小,在这么大的矩形区域内,花生的产量最大会是多少。 首先尝试用最简单的遍历: 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 #include <iostream> using namespace std; int cc(int **matp, int m, int l, int h); int main() { int m, n, l, h; cin >> m >> n; int **matp = new int*[m]; for (int i = 0; i < m; i++) { *(matp + i) = new int[n]; } for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { cin >> matp[i][j]; } } cin >> l >> h; int max = 0; int temp = 0; for (int i = 0; i < m-l+1; i++) { for (int j = 0; j < n-h+1; j++) { temp = cc(matp + i, j, l, h); max = (temp >= max) ? temp : max; } } cout << max; return 0; } int cc(int **matp, int xa, int l, int h) { int ret = 0; for (int i = 0; i < l; i++) { for (int j = 0; j < h; j++) { ret += matp[i][j + xa]; } } return ret; } 发现反馈是6/10 [Time Limit Exceeded] 本身在搜索数组很大的时候,同一个数上要重新计算次,浪费了很多时间。 后来想到,可以采用动窗法来解决。 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 //constructed using some macro and snippets #include <iostream> using namespace std; int main() { //using glide window method. int mat_h, mat_l; cin >> mat_h >> mat_l; //DynMatName:Pmat //Lines:mat_hrows:mat_l int **Pmat = new int *[mat_h]; for (int i = 0; i < mat_h; i++) *(Pmat + i) = new int[mat_l]; int *Pmat_cfg = new int(mat_h); //End of Dynmat. for (int i = 0; i < mat_h; i++) for (int j = 0; j < mat_l; j++) cin >> Pmat[i][j]; int srch_l, srch_h; cin >> srch_h >> srch_l; //DynMatName:Resmat //Lines:mat_h-srch_h+1rows:mat_l-srch_l+1 int **Resmat = new int *[mat_h - srch_h + 1]; for (int i = 0; i < mat_h - srch_h + 1; i++) *(Resmat + i) = new int[mat_l - srch_l + 1](); int *Resmat_cfg = new int(mat_h - srch_h + 1); //End of Dynmat. //construct 1st window for (int i = 0; i < srch_h; i++) for (int j = 0; j < srch_l; j++) Resmat[0][0] += Pmat[i][j]; //construct horiz screener for (int j = 1; j < mat_l - srch_l + 1; j++) { Resmat[0][j] = Resmat[0][j - 1];//get from left for (int i = 0; i < srch_h; i++) { //column process Resmat[0][j] -= Pmat[i][j - 1]; Resmat[0][j] += Pmat[i][srch_l + j - 1]; } } //screen for (int i = 1; i < mat_h - srch_h + 1; i++) { //1st window Resmat[i][0] = Resmat[i - 1][0];//get from upside for (int j = 0; j < srch_l; j++) { //row process Resmat[i][0] -= Pmat[i - 1][j]; Resmat[i][0] += Pmat[i + srch_h - 1][j]; } //screener for (int j = 1; j < mat_l - srch_l + 1; j++) { Resmat[i][j] = Resmat[i][j - 1];//left for (int z = 0; z < srch_h; z++) { //column Resmat[i][j] -= Pmat[i + z][j - 1]; Resmat[i][j] += Pmat[i + z][srch_l + j - 1]; } } } //search int max = 0; for (int i = 0; i < mat_h - srch_h + 1; i++) for (int j = 0; j < mat_l - srch_l + 1; j++) max = max < Resmat[i][j] ? Resmat[i][j] : max; cout << max; //delete dynamic variable //Release DynMat //Name:Resmat for (int i = 0; i < *Resmat_cfg; i++) delete [] *(Resmat + i); delete [] Resmat;delete Resmat_cfg; //End of Release. //Release DynMat //Name:Pmat for (int i = 0; i < *Pmat_cfg; i++) delete [] *(Pmat + i); delete [] Pmat;delete Pmat_cfg; //End of Release. return 0; } 仍然是9/10超时。 后来看到,如果不使用原数组,而是建立一个前缀和数组(Cf. FineArtz, 2018),用来存储从(0,0)到这个点的所有数字和。这样计算取得了比较好的效果。 容易分析得到,动窗法适合一个固定大小的窗户的情形,但是在窗户的大小会发生变化时,需要重新对整个窗户进行计算。在此题中,窗户按不同大小有n^2个,这样做效率会比较低。 而采用前缀和的方式,在经过O(n)的one-pass计算后,每次取值都是O(1)的。 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 <iostream> using namespace std; int main() { int m, n; cin >> m >> n; //DynMatName:Summat //Lines:m+1rows:n+1 int **Summat = new int *[m + 1]; for (int i = 0; i < m + 1; i++) *(Summat + i) = new int[n + 1](); int *Summat_cfg = new int(m + 1); //End of Dynmat. int getnum; for (int i = 1; i <= m; i++) for (int j = 1; j <= n; j++) { cin >> getnum; Summat[i][j] = getnum + Summat[i - 1][j] + Summat[i][j - 1] - Summat[i - 1][j - 1]; } int l, h; cin >> l >> h; int max = 0; int total = 0; for (int i = 0; i < m + 1 - l; i++) for (int j = 0; j < n + 1 - h; j++) { total = (Summat[i + l][j + h] + Summat[i][j] - Summat[i + l][j] - Summat[i][j + h]); max = total > max ? total : max; } cout << max; //Release DynMat //Name:Summat for (int i = 0; i < *Summat_cfg; i++) delete[] * (Summat + i); delete[] Summat; delete Summat_cfg; //End of Release. return 0; } 这么久重新回来重写这篇文章,其实优化一个算法的核心就是要减少重复,拿空间换时间。(这个题目甚至没有使用更多的空间)有名的Strassen算法,仅仅是减少了一次计算,都能够带来很大的 理论上的收益。(虽然现实意义很小,还不如硬算)

2020/1/21
articleCard.readMore

非类型模板参数

在泛型的学习中我们用到了类型模板。例如以下程序片段: 1 2 3 4 5 6 7 8 9 10 template<typename T> class foo{ T bar; ... } template<typename S> S fec(S arg){ ... } 但是template的用法不止于此。还有一种非类型模板。这种方法可以减小程序的开销,增强代码的复用性。 例如以下程序片段: 1 2 3 4 template<typename C,int N> void sort(C (&pos)[N]){ ... } 这个方法可以完整地传入一个数组,而不是退化的指针,包括数组长度都被传入,这样有利于预防数组越界的问题。 当然,这样的方法只允许我们传入一个数组名,而不是首地址指针,因为template的实例化是在预编译时完成的,数组必须是固定长度申请的,因此c++的新特性,即变量长度定义的数组在这里不适用。 由此看来,typename和nullptr有一种设计哲学上的共性,他们通过对全体类型的抽象,而在使用时隐式地转换为所调用的具体类型。相同的,他们的实现都是在预编译的过程中实现的,其本身只是语法上的助记符而已。

2020/1/17
articleCard.readMore

判断的短路规则

所谓的判断short circuiting,是指在条件判断式中,如果前者已经满足了先决条件,就不会执行后置的表达式。例如下面这个程序: 1 2 3 4 5 6 7 int x = 0; if (false && ++x) cout << x; if (true || ++x) cout << x; cout << x; return 0; 屏幕输出为00。这个操作不是编译时处理,而是程序运行时处理的,因此,即便是换成以下程序 1 2 3 4 5 6 7 8 int x; std::cin >> x; if (x!=0 && ++x) std::cout << x; if (x==0 || ++x) std::cout << x; std::cout << x; return 0; 当输入0时也仍然输出00。判断的实现是基于条件下的跳转,而不是编译器优化。

2020/1/16
articleCard.readMore

CodeBlocks重装

我今天为了装wxwidgets,把codeblocks重装了三遍,但是codeblocks自身的卸载不完全,重装了之后基本没有什么区别。查资料发现,重新设置codeblocks,需要手动删除 %APPDATA%\CodeBlocks 文件夹。

2019/11/11
articleCard.readMore

C-Style String Operation

Here introduces C-style 'string' (mostly char[]) operation. It's important that with classes and many more useful and Memory Safe functions, string shall not be processed with these functions. strcpy(dst,source),return dstcopy source to destination, returning the address of destination.be cautious that it can cause memory leak.example: char b[5]={0}; char a[]="abcdefghijklmn"; cout << (int)strcpy(b,a);you'll get an address. cout << ' '; for(int i = 0;i<=15;i++)cout<<b[i]; returns: 1 2 6422204 abcdefghijklmn# #:memory leak^ strncpy(dst,src,n),return dstcopy n characters at most from source to destination. returning the address of destination. (better than strcpy to avoid memory leak.) strcat(dst,src),return dstadd src to the end of dst. Returning the address of dst. strncat(dst,src,n),return dst analogise with 2. strlen(s),return intlength of a string(to the \0, not the size of a list). strcmp(s1,s2),return intreturn (s1-s2). strncmp(s1,s2,n),return int,anal. with 2. strchr(s,ch),return addrreturn the address of the first ch in s. example: char a[]="abcdefghijklmn"; cout<<strchr(a,'f');returns:6422278 example: char a[]="abcdefghijklmn"; cout<<(int)strchr(a,'q');returns:0 strrchr(s,ch),return addrreturn the address of the last ch in s. strstr(s1,s2),return addranal with 7,but finding s2.

2019/10/29
articleCard.readMore