Frey's blog
一个简单的个人博客,用于记录笔记
Zola
2023-10-14T00:00:00+00:00
https://blog.vhcffh.com/atom.xml
日语五十音-历史
2023-10-14T00:00:00+00:00
2023-10-14T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2023/ri-yu-wu-shi-yin-li-shi/
<p>五十音是日语的基础,主要分为平假名和片假名。每个平假名都有其对应的片假名。平假名是由中国古代文字的草率演变而来;片假名是由中国古代文字的偏旁发展而来。记录一下日文假名和汉字的对照关系。</p>
<!--more-->
<h2 id="ping-jia-ming">平假名</h2>
<p><img src="https://blog.vhcffh.com/2023/ri-yu-wu-shi-yin-li-shi/%E4%BA%94%E5%8D%81%E9%9F%B3%E5%B9%B3%E5%81%87%E5%90%8D%E4%B8%AD%E6%96%87%E5%AF%B9%E7%85%A71.png" alt="五十音平假名中文对照1" />
<img src="https://blog.vhcffh.com/2023/ri-yu-wu-shi-yin-li-shi/%E4%BA%94%E5%8D%81%E9%9F%B3%E5%B9%B3%E5%81%87%E5%90%8D%E4%B8%AD%E6%96%87%E5%AF%B9%E7%85%A72.png" alt="五十音平假名中文对照2" /></p>
<h2 id="pian-jia-ming">片假名</h2>
<p><img src="https://blog.vhcffh.com/2023/ri-yu-wu-shi-yin-li-shi/%E4%BA%94%E5%8D%81%E9%9F%B3%E7%89%87%E5%81%87%E5%90%8D%E4%B8%AD%E6%96%87%E5%AF%B9%E7%85%A7.png" alt="五十音片假名中文对照"" /></p>
Linux串口挂载失败
2023-08-28T00:00:00+00:00
2023-08-28T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2023/linuxchuan-kou-gua-zai-shi-bai/
<p>Ubuntu插上CH340的usb转串口后,使用<code>lsmod</code>查看ch341驱动正常加载,<code>lsusb</code>也可以看到usb已经正常加载,但是没有<code>/dev/ttyUSB0</code></p>
<p>查看系统log</p>
<pre><code>kernel: usb 1-3: new full-speed USB device number 5 using ohci-pci
kernel: usb 1-3: New USB device found, idVendor=1a86, idProduct=7523, bcdDevice= 2.64
kernel: usb 1-3: New USB device strings: Mfr=0, Product=2, SerialNumber=0
kernel: usb 1-3: Product: USB Serial
kernel: ch341 1-3:1.0: ch341-uart converter detected
kernel: usb 1-3: ch341-uart converter now attached to ttyUSB0
mtp-probe[54666]: checking bus 1, device 5: "/sys/devices/pci0000:00/0000:00:06.0/usb1/1-3"
mtp-probe[54666]: bus: 1, device: 5 was not an MTP device
snapd[766]: udevmon.go:149: udev event error: Unable to parse uevent, err: cannot parse libudev event: invalid env data
systemd[1]: Starting Braille Device Support...
systemd-udevd[54665]: ttyUSB0: Conflicting device node '/dev/ttyUSB0' found, link to '/dev/ttyUSB0' will not be created.
snapd[766]: hotplug.go:200: hotplug device add event ignored, enable experimental.hotplug
brltty[54669]: BRLTTY 6.4 rev BRLTTY-6.4 [https://brltty.app/]
brltty[54669]: BRLTTY 6.4 rev BRLTTY-6.4 [https://brltty.app/]
brltty[54669]: executing as the invoking user: root
brltty[54669]: brltty: executing as the invoking user: root
mtp-probe[54678]: checking bus 1, device 5: "/sys/devices/pci0000:00/0000:00:06.0/usb1/1-3"
mtp-probe[54678]: bus: 1, device: 5 was not an MTP device
snapd[766]: udevmon.go:149: udev event error: Unable to parse uevent, err: cannot parse libudev event: invalid env data
brltty[54669]: BrlAPI Server: release 0.8.3
brltty[54669]: brltty: BrlAPI Server: release 0.8.3
systemd[1]: Started Braille Device Support.
brltty[54669]: Linux Screen Driver:
brltty[54669]: brltty: Linux Screen Driver:
kernel: input: BRLTTY 6.4 Linux Screen Driver Keyboard as /devices/virtual/input/input10
brltty[54669]: USB configuration set error 16: 设备或资源忙
brltty[54669]: brltty: USB configuration set error 16: 设备或资源忙
kernel: usb 1-3: usbfs: interface 0 claimed by ch341 while 'brltty' sets config #1
systemd-logind[768]: Watching system buttons on /dev/input/event8 (BRLTTY 6.4 Linux Screen Driver Keyboard)
brltty[54669]: USB interface in use: 0 (ch341)
brltty[54669]: brltty: USB interface in use: 0 (ch341)
ModemManager[964]: <info> [base-manager] port ttyUSB0 released by device '/sys/devices/pci0000:00/0000:00:06.0/usb1/1-3'
kernel: ch341-uart ttyUSB0: ch341-uart converter now disconnected from ttyUSB0
kernel: ch341 1-3:1.0: device disconnected
ModemManager[964]: <info> [base-manager] couldn't check support for device '/sys/devices/pci0000:00/0000:00:06.0/usb1/1-3': Operation was cancelled
brltty[54669]: NoSpeech Speech Driver:
brltty[54669]: brltty: NoSpeech Speech Driver:
</code></pre>
<p>像是程序brltty占用了usb,卸载brltty后恢复正常</p>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://forum.mysensors.org/topic/8871/unable-to-connect-to-dev-ttyusb0-solved">Unable to connect to /dev/ttyUSB0 [Solved] | MySensors Forum</a></li>
<li><a href="https://askubuntu.com/questions/1403705/dev-ttyusb0-not-present-in-ubuntu-22-04">drivers - /dev/ttyUSB0 not present in Ubuntu 22.04 - Ask Ubuntu</a></li>
</ol>
SNR与EVM的关系
2023-08-17T00:00:00+00:00
2023-08-17T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2023/snryu-evmde-guan-xi/
<h2 id="guan-xi">关系</h2>
<p>当SNR较大(大于10dB)时,<strong>SNR=-20lg(EVM)</strong>,其中SNR的单位为dB,EVM为正实数。</p>
<p>如果EVM用dB表示,可以简化为<strong>SNR = -2*EVM_dB</strong>。(此式在SNR大于4dB时,误差较小,不超过1dB)</p>
<p>下面依次介绍 log、dB、dBm、evm、snr的概念,再说明此结论的推导过程。</p>
<h2 id="dui-shu-gong-shi">对数公式</h2>
<p>如果a^b=N(a>0,且a≠1,则b叫做以a为底N的对数,公式如下,其中a叫做底数,N叫做真数, b叫对数。</p>
<p><img src="https://blog.vhcffh.com/2023/snryu-evmde-guan-xi/index.png" alt="对数" /></p>
<p>通常我们将以10为底的对数叫常用对数,以e为底的对数叫自然对数。</p>
<p>常用对数:$ lg(b) = log_{10}(b) $ (10为底数)。</p>
<p>自然对数:$ ln(b) = log_{e}(b) $ (e为对数)。</p>
<p>对数的基本性质中,下面的结论经常用到:</p>
<p>$$
\begin{array}{l}
log_{a}M^n = nlog_{a}M \cr
lg10 = 1 \cr
lg2 \approx 0.3 \cr
lg1 = 0
\end{array}
$$</p>
<h2 id="xin-hao-qiang-du">信号强度</h2>
<p>日常中通常使用功率来衡量一个电器做功的快慢,如一个10W的电灯泡,10W功率就是电灯泡消耗能量做功的快慢。</p>
<p>在天线收发系统里,同样也需要消耗电能来转换为电磁波的能量进行传输。</p>
<p>但是电磁波的能量衰减非常快,例如一个100mW的能量源,传输一段距离后很快就能衰减成1mW、0.1mW、0.01mW甚至更小。</p>
<p>对于这种呈几何数量级的衰减,使用功率来衡量会给计数带来不便,因此引用新的概念:dB和dBm。</p>
<h3 id="dbjie-shao">dB介绍</h3>
<p>dB是一个表征相对值的值,纯粹的比值,只表示<strong>两个量的相对大小关系,没有单位</strong>。公式为dB=10lg(A/B),<strong>一般作为(SNR)信噪比、损耗的单位</strong>。</p>
<p>当考虑甲的功率相比于乙功率大或小多少个dB时,按下面的计算公式:10log(甲功率/乙功率)。</p>
<p>例如A的功率为100mW,B的功率为10mW,则10lg(100 / 10) = 10dB,说明A比B大10dB。</p>
<blockquote>
<p>例:如何理解:+3 dB表示增大为两倍,-3 dB表示下降为1/2。
如果A的功率开始为100mW,经过衰减变成了50mw,则10lg(50 / 100) = 10lg(1/ 2) = -10lg(2)= -3dB。
如果A的功率开始为100mW,经过放大变成了200mw,则10lg(200 / 100) = 10lg(2) = 3dB。</p>
</blockquote>
<h3 id="dbmjie-shao">dBm介绍</h3>
<p>dBm是一个表示功率绝对值(可以认为是以1mW功率为基准的一个比值),计算公式为:10log(功率值/1mw)。</p>
<p>记住一个口诀: 加3乘2, 加10乘10; 减3除2,减10除10 (+3 dB,表示功率增加为2倍;+10 dB,表示功率增加为10倍。-3 dB,表示功率减小为1/2;-10 dB,表示功率减小为1/10)。</p>
<blockquote>
<p>例:功率P为1mw,折算为dBm后为0dBm。
40W的功率,按dBm单位进行折算后的值应为:
10log(40W/1mw)=10log(40000)= 46dBm。 按口决: 40W = 1w * (( 10 * 10 * 10)W * 10 * 2 * 2) => 10 + 10 + 10 + 10 + 3 + 3 = 46dBm
那么44dBm 是多少W? (25W)</p>
</blockquote>
<p>需要注意的是,对于功率的增益,我们用10lg(Po/Pi),而对于电压和电流的增益,要用20lg(Vo/Vi)、20lg(Io/Ii)。</p>
<p>原因是 这个2来源于电功率转换公式的平方上:</p>
<p>$$
P = UI = I^2R = \frac {U^2} {R}
$$</p>
<h2 id="snrhe-evm">SNR和EVM</h2>
<h3 id="snrjie-shao">SNR介绍</h3>
<p>信噪比SNR(Signal-to-noise Ratio),指的是系统中信号与噪声的比。公式为 SNR = 10lg(Ps / Pn),其中:</p>
<ul>
<li>SNR:信噪比,单位是dB。</li>
<li>Ps:信号的有效功率。</li>
<li>Pn:噪声的有效功率。</li>
</ul>
<p>$$
SNR(dB) = 10 *lg \frac {\int s(t)^2 \mathrm{d}t} {\int n(t)^2 \mathrm{d}t} =
10 * lg \frac {\int s(t)^2 \mathrm{d}t} {\int (x(t)-s(t))^2 \mathrm{d}t}\tag{1}
$$</p>
<p>式中$ x(t) $ 为矢量reference signal,$ s(t) $ 为矢量输入信号。</p>
<h3 id="evmjie-shao">EVM介绍</h3>
<p><img src="https://blog.vhcffh.com/2023/snryu-evmde-guan-xi/EVM.png" alt="EVM" /></p>
<p>QAM 调制信号通常用其EVM 来衡量信号质量,EVM是英文Error Vector Magnitude缩写,意为误差向量幅度。</p>
<p>误差矢量Error Vector 的幅度与参考信号Reference Signal 幅度的比值。通常测量的EVM为其RMS 值(Root Mean Square均方根),计算公式如:</p>
<p>$$
EVM=\sqrt {\frac {\int (x(t)-s(t))^2 \mathrm{d}t} {\int x(t)^2 \mathrm{d}t}}\tag{2} $$</p>
<p>式中$ x(t) $ 为矢量reference signal,$ s(t) $ 为矢量输入信号。</p>
<p>大多时候,EVM用dB表示:</p>
<p>$$
EVM_{dB} = 10*lg{EVM}
$$</p>
<h3 id="snryu-evmguan-xi">SNR与EVM关系</h3>
<p>从(1)式 和(2)式可以看出:当矢量输入信号$ s(t) $越接近于矢量reference signal $ x(t) $,即$ s(t) $可以用$ x(t) $代替,推导如下:</p>
<p>$$
\begin{array}{l}
SNR(dB) = 10 * lg \frac {\int s(t)^2 \mathrm{d}t} {\int (x(t)-s(t))^2 \mathrm{d}t} \cr
\approx 10 * lg \frac {\int x(t)^2 \mathrm{d}t} {\int (x(t)-s(t))^2 \mathrm{d}t} \cr
= -1 * 10 * lg \frac {\int (x(t)-s(t))^2 \mathrm{d}t} {\int x(t)^2 \mathrm{d}t} \cr
= -10 * lg({EVM}^2) \cr
=-2 * 10 * lg(EVM)
=-2EVM_{dB}
\end{array}
$$</p>
C语言必备知识
2023-07-08T00:00:00+00:00
2023-07-08T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2023/everything-i-wish-i-knew-when-learning-c/
<p>学习C语言相当困难。不是因为C语言难学,而是因为用C编程需要知道很多其它类型的知识。例如</p>
<ul>
<li>C语言没有消除平台或操作系统差异的环境;你也需要了解你的平台。</li>
<li>有许多C语言编译器选项和构建工具,即使是运行一个简单的程序也要做出许多决定。</li>
<li>有一些与CPU、操作系统、一般的编译代码有关的重要概念</li>
<li>与其他语言相比,C语言的使用方式多种多样,所以集中的"社区"或风格要少得多。</li>
</ul>
<h2 id="you-yong-de-zi-yuan">有用的资源</h2>
<ul>
<li><a href="https://www.tutorialspoint.com/cprogramming/index.htm">TutorialsPoint C</a>:基础的介绍</li>
<li><a href="https://github.com/oz123/awesome-c">awesome-c</a>:一些有用的库和工具</li>
<li><a href="https://en.cppreference.com/w/c">cppreference</a>:C语言和标准库的技术参考</li>
</ul>
<h2 id="ke-yi-xue-xi-de-hao-xiang-mu">可以学习的好项目</h2>
<p>学习一些小的,只包含C语言的项目是很有用的:</p>
<ul>
<li><a href="https://github.com/whymirror/bloopsaphone/blob/tree/master/c">Bloopsaphone</a>:一个用于合成声音的Ruby库,它的核心是一个小型的C模块。有少量的概念和良好的结构。</li>
<li><a href="https://github.com/antirez/sds">Simple Dynamic Strings (sds)</a>:一个很好的例子,只有一个.c和.h文件,就能实现复杂的资源管理。</li>
<li><a href="https://github.com/tmewett/BrogueCE">Brogue CE</a>:一个roguelinke的视频游戏,很大单并不完美,有30k+行,贡献它可以很好的磨砺你的C语言。</li>
<li><a href="https://github.com/nothings/stb">stb single-file libraries</a>:这些是中小型的模块,旨在高度便携,包括针对嵌入式设备和游戏机。</li>
</ul>
<h2 id="bian-yi-lian-jie-tou-wen-jian-he-fu-hao-biao">编译,链接,头文件和符号表</h2>
<p>这是一些关于编译C语言的一些基础知识,这对理解其它事情很有帮助。</p>
<p>C代码是写在.c源文件中的。每个源文件都被编译成一个.o对象文件,它就像是.c文件中编译后的功能代码的容器。它们是不可执行的。对象文件内部有一个符号表,是该文件中定义的全局函数和变量的名称。</p>
<pre data-lang="sh" class="language-sh "><code class="language-sh" data-lang="sh"># compile to objects
cc -c thing.c -o thing.o
cc -c stuff.c -o stuff.o
</code></pre>
<p>源文件是完全独立的,可以并行地编译成对象。</p>
<p>为了跨文件使用函数和变量,我们使用头文件(.h)。这些只是以特定方式使用的普通C源文件。记得上面说过,对象文件只包含全局函数和变量的名称,没有类型、宏,甚至函数参数。为了跨文件使用符号,我们需要指定所有需要使用它们的额外信息。我们把这些"声明"放在自己的.h文件中,这样其他的.c文件就可以通过<code>#include</code>引用它们了。</p>
<p>为了避免重复,一个.c文件通常不会定义自己的类型/宏等,而只是包括自己或其所属模块/组件的头文件。</p>
<p><strong>把头文件看作是一个API的规范</strong>,它可以在任何数量的源文件中实现。你甚至可以为不同的平台或目的,编写同一头文件的不同实现。</p>
<p>当编译一个仅被声明(例如被包含的头文件)而未被定义的符号的引用时,对象文件将标记这个符号是缺失的,需要被实现。</p>
<p>将一个或多个对象连接在一起的最后工作,匹配所有的符号引用,是由编译器的 "链接器 "组件完成的。链接器输出完整的可执行文件或共享库。</p>
<pre data-lang="sh" class="language-sh "><code class="language-sh" data-lang="sh"># link objects to executable
cc thing.o stuff.o -o gizmo
</code></pre>
<p>总之,我们在C语言中不像其他语言那样"包含"其他源文件。我们只包括声明,然后代码被链接器匹配起来。</p>
<h2 id="bu-yao-shi-yong-zhe-xie-han-shu">不要使用这些函数</h2>
<p>C语言已经很老了,并试图做到高度向后兼容。因此,它有一些应该被避免的功能。</p>
<ul>
<li><code>atoi()</code>,<code>atol()</code>等类似函数;发生错误时,它们返回0,但是0也是一个有效的值。更好的是使用<code>strtoi()</code>等函数。</li>
<li><code>gets()</code>是不安全的,因为它没有给出目标缓冲区的界限。最好使用<code>fgets()</code>。</li>
</ul>
<h2 id="shu-zu-bu-shi-zhi">数组不是值</h2>
<p>重要的是要意识到C语言作为一种语言,只处理已知大小的数据块。你也许可以把C语言概括为 "复制已知大小的值的语言"。</p>
<p>我可以在程序中传递一个整数或一个结构,从函数中返回它们,等等,并将它们视为适当的对象,因为C知道它们的大小,因此可以编译代码来复制它们的全部数据。</p>
<p>我不能用数组来做这个。当我在一个函数中声明一个<code>int[5]</code>类型的变量时,实际上我并没有得到一个<code>int[5]</code>类型的值;我得到的是一个<code>int*</code>值,其中有5个int被分配到它那里。由于这只是一个指针,应当由程序员而不是语言管理复制它后面的数据并保持其有效性。</p>
<p>然而,结构体内的数组被视为值,并与结构体一起被完全复制。</p>
<p>(从技术上讲,已知大小的数组类型是真实的类型,而不仅仅是指针;例如,sizeof会告诉你整个数组的大小。但是你不能把它们当作自带的值)。</p>
<h2 id="zhong-yao-de-bian-yi-xuan-xiang">重要的编译选项</h2>
<p>编译器有这么多的选项,而默认的选项并不是很好。下面是你可能需要的绝对必要的标志。(它们是以GCC/Clang风格给出的;其他编译器的语法可能有所不同)。</p>
<ul>
<li><code>-O2</code>:为发布版本的构建优化代码</li>
<li><code>-g -Og</code>:用于调试构建;为调试器启用额外信息,并为调试进行优化。</li>
<li><code>-Wall</code>:启用所有警告;也可以通过<code>-Wno-...</code>禁用指定警告。</li>
<li><code>-Werror</code>:将所有警告转换为错误;确保至少启用<code>-Werror=implicit</code>,确保调用未声明函数时报错。</li>
<li><code>-DNAME</code>和<code>-DNAME=value</code>:生命一个宏。</li>
<li><code>-fsanitize=address,undefined</code>:用于调试构建;使能两个sanitizer,它在整个编译后的代码中注入额外的检查以发现错误。详见<a href="https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html">GCC编译选项</a>。</li>
<li><code>-std=...</code>:选择一个标准。在大多数情况下,你可以省略它,使用你的编译器的默认值(通常是最新的标准)。那些对可移植性有特殊关注的人可以使用<code>-std=c89</code>来保持"经典"C。</li>
</ul>
<p>参考<a href="https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html">Option Summary (Using the GNU Compiler Collection (GCC))</a>,了解更多编译选项。</p>
<h2 id="san-chong-lei-xing-de-nei-cun-yi-ji-he-shi-shi-yong">三种类型的内存以及何时使用</h2>
<ul>
<li><strong>自动存储</strong>是存储局部变量的地方。当一个函数被调用时,将为其创建一个新的自动存储区域,并在其返回时删除。只有返回值被保留;它被复制到调用它的函数的自动存储中。这意味着返回一个指向局部变量的指针是不安全的,因为底层数据会被悄悄地删除。自动存储通常被称为<strong>堆栈</strong>。</li>
<li><strong>分配的存储空间</strong>是使用<code>malloc()</code>的得到的。它一直存活到被<code>free()</code>的时候,所以可以被传递到任何地方,包括向上传递到调用函数。它通常被称为<strong>堆</strong>。</li>
<li><strong>静态存储</strong>在程序的生命周期内有效。它是在进程开始时分配的。全局变量被存储在这里。</li>
</ul>
<p>如果你想从一个函数中 "返回 "内存,你不必使用malloc/分配的存储;你可以传递一个指向本地数据的指针:</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">void getData(int *data) {
data[0] = 1;
data[1] = 4;
data[2] = 9;
}
void main() {
int data[3];
getData(data);
printf("%d\n", data[1]);
}
</code></pre>
<h2 id="ming-ming-xi-guan">命名习惯</h2>
<p>C语言不支持命名空间。如果你要做一个公共库,或者希望一个"模块"有一个名字,你需要选择一个前缀来添加到所有公共API的名字中:</p>
<ul>
<li>函数</li>
<li>类型</li>
<li>枚举值</li>
<li>宏</li>
</ul>
<p>此外,你应该总是为每个枚举包括一些不同的前缀,这样你就知道这个值属于哪个枚举类型:</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">enum color {
COLOR_RED,
COLOR_BLUE,
...
}
</code></pre>
<p>关于名字没有真正的标准,例如,<code>snake_case</code>或者<code>cameCase</code>。选择并保持一致,我所知道的最接近约定的东西是,有些人把类型命名为<code>my_type_t</code>,因为许多标准的C类型都是这样的(<code>ptrdiff_t</code>,<code>int32_t</code>, 等等)。</p>
<h2 id="static">static</h2>
<p>在一个函数或文件内变量上使用,<code>static</code>使其成为文件本地的。它不会被导出为一个符号,供其他源文件使用。</p>
<p><code>static</code>也可以用在局部变量上,这使得变量在调用该函数时持续存在。你可以认为这就像一个全局变量,它只在一个函数的范围内。这对于计算和存储数据以便在随后的调用中重复使用是很有用的;但请记住,这也有全局/共享状态,需要在多线程或递归调用中注意他们的使用。</p>
<p>(它看起来有多重含义,因为在全局作用域中它似乎减少了变量的作用域,但在函数作用域中它又增加了变量的作用域)。实际上,在这两种情况下,它所做的是使它们与文件相联系。</p>
<h2 id="jie-gou-ti-fang-fa-can-shu">结构体方法参数</h2>
<p>如果你在C语言之前学过一种更有特点的语言,你可能会发现很难想象如何转化这些知识。这里有一个类似于面向对象编程的常用方法:"结构体方法"。你写的函数接收指向结构的指针,以改变它们或获得属性:</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">typedef struct {
int x;
int y;
} vec2;
void vec_add(vec2 *u, const vec2 *v) {
u->x += v->x;
u->y += v->y;
}
int vec_dot(const vec2 *u, const vec2 *v) {
return u->x * v->x + u->y * v->y;
}
</code></pre>
<p>你不能扩展结构或做任何真正类似于OO的事情,但这是一个有用的模式,可以用来参考。</p>
<h2 id="const">const</h2>
<p>将一个<code>T</code>类型的变量或参数声明为<code>const T</code>,大致上意味着该变量不能被修改。这意味着它不能被赋值,如果T是一个指针或数组类型,它也不能被改变。</p>
<p>将类型为<code>T</code>的变量等于类型为<code>const T</code>的变量是允许的,反过来是不允许的。</p>
<p>一个好的习惯是默认将函数的指针参数声明为常数,只有在需要修改它们时才省略。</p>
<h2 id="ping-tai-yu-biao-zhun-apis">平台与标准APIs</h2>
<p>当你通过<code>#include<some_header.h></code>引用头文件时,一般无法确定它们依赖什么。通常来自以下情况:</p>
<ul>
<li>标准C库,缩写为"stdlib"。例如:<code>stdio.h</code>,<code>stdlib.h</code>,<code>error.h</code>
<ul>
<li>这是语言规范的一部分,应该由所有符合要求的平台和编译器来实现。非常安全的依赖。 </li>
<li><a href="https://en.cppreference.com/w/c/header">https://en.cppreference.com/w/c/header</a></li>
</ul>
</li>
<li>POSIX,一套标准的操作系统APIs。例如:<code>unistd.h</code>,<code>sys/time.h</code>
<ul>
<li>通常由Linux,macOS,BSDs实现。</li>
<li>windows默认情况下不可用。可以通过<a href="https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-headers/crt/">MinGW</a>进行支持,对于更完整的支持,需要<a href="https://cygwin.com/">Cygwin</a>库。</li>
</ul>
</li>
<li>非标准的操作系统接口
<ul>
<li>Linux特殊APIs,可以通知<a href="https://www.man7.org/linux/man-pages/index.html">man文档</a>第三章了解。</li>
<li><a href="https://docs.microsoft.com/en-us/windows/win32/api/">Windows Win32</a></li>
<li><a href="https://developer.apple.com/documentation/technologies">Mac's OS APIs</a></li>
</ul>
</li>
<li>安装在标准位置的一些第三方库。</li>
</ul>
<p>通过一个平台中立的头文件与你的特定平台代码对接可能是一个好主意,这样它就可以用不同的方式来实现。很多流行的C语言库基本上都是统一的、精心设计的对特定平台功能的抽象。</p>
<h2 id="zheng-xing">整型</h2>
<p>整数在C语言中是经常被抱怨的,编写正确的代码需要一些小心:</p>
<h3 id="sizes">Sizes</h3>
<p><a href="https://en.cppreference.com/w/c/language/arithmetic_types">所有的整型有一个默认的最小size</a>.在常见的平台上,有些比它们的最小size更大,例如int,尽管最小size是16位,但在Windows、macOS和Linux上是32位。在编写可移植代码时,你必须假设整数永远不能超过其最小size。</p>
<p>如果你想精确控制整数大小,你可以使用<code>stdint.h</code>中的标准类型,如<code>int32_t</code>、<code>uint64_t</code>等。还有<code>_least_t</code>和<code>_fast_t</code>类型。例如: <code>uint_fast8_t</code>、<code>uint_least8_t</code>。</p>
<ul>
<li><code>_least_t</code>:表示至少保存多少字节。</li>
<li><code>_fast_t</code>:表示让编译其选择一个可以容纳多少字节并且操作最快。</li>
</ul>
<p>需要在任何地方使用这些明确的类型吗?必须承认我对这个问题很纠结,但我越想越觉得你应该这样做--没有什么坏处。你真正不应该这样做的唯一原因是在制作API时必须使用缺少<code>stdint.h</code>的老式C89编译器。使用<code>int16_fast_t</code>或其他类型而不是<code>int</code>,不会更糟,只会更清楚。</p>
<h3 id="suan-shu-he-you-hua">算数和优化</h3>
<p><a href="https://en.cppreference.com/w/c/language/operator_arithmetic">C语言中的算术受制于许多奇怪的规则</a>,这些规则可能会产生意想不到的或无法实现的结果。<a href="https://en.cppreference.com/w/c/language/conversion#Integer_promotions">整数提升</a>尤其需要注意。</p>
<p>可以参考<a href="https://www.nayuki.io/page/summary-of-c-cpp-integer-rules">Nayuki’s summary of C integer rules</a>。</p>
<h3 id="wu-fu-hao-char">无符号char</h3>
<p>所有其他的整数类型都默认为有符号,但char可以是有符号或无符号的,这取决于平台的情况。因此,它只有在用于字符串时才是可移植的;如果你想要一个小的/最小的8位的数字,也要指定符号。</p>
<h2 id="hong-yu-constbian-liang">宏与const变量</h2>
<p>要定义简单的常数,你有两个选择:</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">static const int my_constant = 5;
// or
#define MY_CONSTANT 5
</code></pre>
<p>区别在于,前者是一个真正的变量,后者是一个复制粘贴的内联表达式。</p>
<ul>
<li>与变量不同,你可以在需要"常量表达式"的情况下使用宏,比如数组长度或开关语句的情况。</li>
<li>与宏不同,你可以得到一个变量的指针。</li>
</ul>
<p>让常量真正成为"常量表达式"是非常有用的,因此它们通常应该被定义为宏。变量更适合于更大或更复杂的值,如结构体实例。</p>
<p>如果你的常数是一个整数,你有第三个更好的选择,即枚举:</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">enum {
MY_CONSTANT = 5
}
</code></pre>
<p>这在C语言中定义了一个常量表达式,而不是在预处理程序中,所以它可以更容易被调试器等看到。</p>
<p>在C23中,你可以给一个枚举类型指定明确的基础类型:</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">enum : size_t {
BUFFER_LENGTH = 1024
}
</code></pre>
<h2 id="hong-yu-nei-lian-han-shu">宏与内联函数</h2>
<p>宏可以有参数,然后可以扩展为C代码。</p>
<p>相对于函数的优势:</p>
<ul>
<li>这些代码被直接粘贴在周围的代码中,而不是编译函数调用指令。这可以使代码更快,因为函数调用有一些开销。</li>
<li>它们可以是类型通用的。例如,<code>x + y</code>是任何数字类型的有效语法。如果我们把它变成一个函数,我们就必须把它们声明为参数,并事先选择它们的类型,即大小和符号性,这将使它只能在某些情况下使用。</li>
</ul>
<p>缺点</p>
<ul>
<li>参数的重复使用。假设我们有一个宏<code>MY_MACRO(x)</code>。如果x在定义中被多次使用,那么表达式x将被多次出现,因为它被简单地复制和粘贴。而函数的参数的表达式被会被计算一次,然后传入函数。</li>
<li>它们可能容易出错,因为它们是在源码级别工作。一般来说,多使用括号是个好主意,总是对整个宏定义本身和任何参数使用括号,这样表达式就不会在无意中合并。</li>
</ul>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">// Instead of:
#define MY_MACRO(x) x+x
// Do:
#define MY_MACRO(x) ((x)+(x))
</code></pre>
<p>除非你需要类型通用,否则你可以通过将一个函数定义为静态内联来获得两方面的好处。内联向编译器提供了一个提示,即函数中的代码应该直接编译到使用它的地方,而不是被调用。你可以把静态内联函数放在头文件中,就像宏一样,没有任何问题。</p>
<p>此外,从C11开始,你可以使用一个特殊的宏<code>_Generic</code>为不同类型的函数提供重载:</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">#define sin(X) _Generic((X), \
long double: sinl, \
default: sin, \
float: sinf \
)(X)
</code></pre>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://tmewett.com/c-tips/#essential-compiler-flags">Everything I wish I knew when learning C - Tom Mewett (tmewett.com)[原文]</a></li>
</ol>
AT&T汇编与Intel汇编区别
2023-04-21T00:00:00+00:00
2023-04-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2023/at-thui-bian-yu-intelhui-bian-qu-bie/
<ol>
<li>前缀不同</li>
</ol>
<p>在Intel的语法中,寄存器和和立即数都没有前缀。
但是在AT&T中,寄存器前需要加上"%",而立即数前需要加上"$"。
在Intel的语法中,十六进制和二进制立即数后缀分别是"h"和"b",而在AT&T中,十六进制立即数前需要加上"0x"。</p>
<table><thead><tr><th>Intel语法</th><th>AT&T语法</th></tr></thead><tbody>
<tr><td>mov eax,8</td><td>movl $8,%eax</td></tr>
<tr><td>mov ebx,0ffffh</td><td>movl $0xffff,%ebx</td></tr>
<tr><td>int 80h</td><td>int $0x80</td></tr>
</tbody></table>
<ol start="2">
<li>操作数方向不同</li>
</ol>
<p>Intel与AT&T操作数的方向正好相反。
在Intel语法中,第一个操作数是目的操作数,第二个操作数源操作数。
而在AT&T中,第一个数是源操作数,第二个数是目的操作数。</p>
<table><thead><tr><th>Intel语法</th><th>AT&T语法</th></tr></thead><tbody>
<tr><td>mov eax,[ecx]</td><td>movl (%ecx),%eax</td></tr>
</tbody></table>
<ol start="3">
<li>内存单元操作数不同</li>
</ol>
<p>在Intel的语法中,基寄存器用"[]"括起来,而在AT&T中,用"()"括起来。</p>
<table><thead><tr><th>Intel语法</th><th>AT&T语法</th></tr></thead><tbody>
<tr><td>mov eax,[ebx+5]</td><td>movl 5(%ebx),%eax</td></tr>
</tbody></table>
<ol start="4">
<li>寻址方式不同</li>
</ol>
<p>Intel的指令格式是segreg:[base+index*scale+disp],而AT&T的格式是%segreg:disp(base,index,scale)。
其中index/scale/disp/segreg全部是可选的,完全可以简化掉。
如果没有指定scale而指定了index,则scale的缺省值为1。
当立即数用在scale/disp中时,不应当在其前冠以"$"前缀。</p>
<table><thead><tr><th>Intel语法</th><th>AT&T语法</th></tr></thead><tbody>
<tr><td>mov eax,[ebx+20h]</td><td>movl 0x20(%ebx),%eax</td></tr>
<tr><td>add eax,[ebx+ecx*2h</td><td>addl (%ebx,%ecx,0x2),%eax</td></tr>
<tr><td>lea eax,[ebx+ecx]</td><td>leal (%ebx,%ecx),%eax</td></tr>
<tr><td>sub eax,[ebx+ecx*4h-20h]</td><td>subl -0x20(%ebx,%ecx,0x4),%eax</td></tr>
</tbody></table>
<ol start="5">
<li>后缀不同
在AT&T的操作码后面有一个后缀,其含义就是指出操作码的大小。
"l"表示长整数(32位),"w"表示字(16位),"b"表示字节(8位)。
而在Intel的语法中,则要在内存单元操作数的前面加上byte ptr, word ptr 和 dword ptr。</li>
</ol>
<table><thead><tr><th>Intel语法</th><th>AT&T语法</th></tr></thead><tbody>
<tr><td>mov al,bl</td><td>movb %bl,%al</td></tr>
<tr><td>mov ax,bx</td><td>movw %bx,%ax</td></tr>
<tr><td>mov eax,ebx</td><td>movl %ebx,%eax</td></tr>
<tr><td>mov eax,dword ptr [ebx]</td><td>movl (%ebx),%eax</td></tr>
</tbody></table>
nmcli配置wifi
2023-01-09T00:00:00+00:00
2023-01-09T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2023/nmclipei-zhi-wifi/
<h2 id="lian-jie-wifi">连接wifi</h2>
<h3 id="sao-miao-wifi">扫描wifi</h3>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">sudo nmcli dev wifi list
</code></pre>
<h3 id="lian-jie-xin-de-wifi">连接新的wifi</h3>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">sudo nmcli dev wifi connect NETWORK-SSID password "NETWORK-PASSWORD"
</code></pre>
<h3 id="cha-kan-bao-cun-de-wifixin-xi">查看保存的wifi信息</h3>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">nmcli con show
</code></pre>
<h3 id="lian-jie-bao-cun-zai-wifi">连接保存在wifi</h3>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">nmcli con up NETWORK-SSID
</code></pre>
<h3 id="duan-kai-wifi">断开wifi</h3>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">nmcli con down NETWORK-SSID
</code></pre>
<h2 id="chuang-jian-wifire-dian">创建wifi热点</h2>
<h3 id="chuang-jian-re-dian">创建热点</h3>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">nmcli connection add type wifi ifname wlan0 con-name local-ap autoconnect yes ssid WIFI-SSID mode ap
</code></pre>
<h3 id="she-zhi-mi-ma">设置密码</h3>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">nmcli connection modify local-ap 802-11-wireless.mode ap 802-11-wireless-security.key-mgmt wpa-psk ipv4.method shared 802-11-wireless-security.psk "PASSWORD"
</code></pre>
<h3 id="qi-dong-re-dian">启动热点</h3>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">nmcli connection up local-ap
</code></pre>
<h3 id="pei-zhi-dhcp">配置dhcp</h3>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash"># vim /etc/dhcp/dhcpd.conf
option domain-name-servers 8.8.8.8;
subnet 192.168.44.0 netmask 255.255.255.0 { # 目标网段
range 192.168.44.128 192.168.44.200; # 具体的IP
option routers 192.168.44.1; # 网关地址
option domain-name-servers 114.114.114.114; # dns解析
}
</code></pre>
<h3 id="qi-dong-dhcpfu-wu">启动dhcp服务</h3>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">systemctl restart dhcpd
</code></pre>
WSL2 启用桥接网络并开启mDNS
2023-01-09T00:00:00+00:00
2023-01-09T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2023/wsl2-bridge-network-and-using-mdns/
<ol>
<li>WSL2 启用桥接网络</li>
</ol>
<p>首先在<code>Hyper-V管理器</code>中创建新的虚拟交换机(假设名字为eth_switch),并选择外部网络和要桥接道德物理网卡。</p>
<p>编辑window主目录下.wslconfig文件,添加两行配置</p>
<pre><code>#C:\Users\<user_name>\.wslconfig
[wsl2]
#...
networkingMode=bridged
vmSwitch=eth_switch
</code></pre>
<p>重启WSL2后,eth0将和物理网卡一样获取一个独立ip</p>
<ol start="2">
<li>启用avahi-daemon.service服务实现mDNS广播</li>
</ol>
<pre><code># 安装
sudo apt-get install avahi-daemon
# 启动
systemctl start avahi-daemon.service
# 配置自动启动
systemctl enable avahi-daemon.service
</code></pre>
<p>配置WSL2计算机名
修改文件<code>/etc/wsl.conf</code></p>
<pre><code>[network]
hostname = wsl
</code></pre>
<p>重启之后便可以在局域网内通过计算机名wsl访问wsl了</p>
<pre><code>PS C:\Users\fly92> ping wsl
正在 Ping wsl.local [192.168.10.164] 具有 32 字节的数据:
来自 192.168.10.164 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.10.164 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.10.164 的回复: 字节=32 时间<1ms TTL=64
来自 192.168.10.164 的回复: 字节=32 时间<1ms TTL=64
192.168.10.164 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 0ms,最长 = 0ms,平均 = 0ms
</code></pre>
<h3 id="can-kao">参考</h3>
<ol>
<li><a href="https://learn.microsoft.com/zh-cn/windows/wsl/wsl-config">WSL 中的高级设置配置</a></li>
</ol>
python实现AES加密
2022-11-13T00:00:00+00:00
2022-11-13T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/python-implements-aes-encryption/
<p>摘要:作为新一代的加密标准,AES 旨在取代 <a href="https://blog.vhcffh.com/python-implements-des-encryption/">DES</a>,以适应当今分布式开放网络对数据加密安全性的要求。
本文在分析了 AES 加密原理的基础上着重说明了算法实现的具体步骤,并用 python 实现了对字符串的加密和解密。</p>
<h1 id="aesjie-shao">AES介绍</h1>
<p>AES(高级加密标准,Advanced Encryption Standard),在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种分组加密标准。这个标准用来替代原先的 DES,目前已经广为全世界所使用,成为对称密钥算法中最流行的算法之一。</p>
<p>在 AES 出现之前,最常用的对称密钥算法是 DES 加密算法,它在 1977 年被公布成为美国政府的商用加密标准。
DES 的主要问题是密钥长度较短,渐渐不适合于分布式开放网络对数据加密安全性的要求。
因此,1998年美国政府决定不再继续延用 DES 作为联邦加密标准,并发起了征集 AES 候选算法的活动。
征集活动对 AES 的基本要求是: 比三重DES快、至少与三重DES一样安全、数据分组长度为128比特、密钥长度为128/192/256比特。</p>
<p>经过三年多的甄选,比利时的密码学家所设计的 Rijndael 算法最终脱颖而出,成为新一代的高级加密标准,并于 2001 年由美国国家标准与技术研究院(NIST)发布于 FIPS PUB 197。</p>
<h1 id="aessuan-fa-yuan-li">AES算法原理</h1>
<p>AES算法(即 Rijndael 算法)是一个对称分组密码算法。
数据分组长度必须是 128 bits,使用的密钥长度为 128,192 或 256 bits。
对于三种不同密钥长度的 AES 算法,分别称为“AES-128”、“AES-192”、“AES-256”。
(Rijndael 的设计还可以处理其它的分组长度和密钥长度,但 AES 标准中没有采用)</p>
<p>下图是 AES 加密解密的整体流程图:</p>
<p><img src="https://blog.vhcffh.com/2022/python-implements-aes-encryption/AES.png" alt="AES" /></p>
<p>这里我们需要知道3个符号:</p>
<ul>
<li>Nb: 状态 State 包含的列(32-bit 字)的个数,也就是说 Nb=4;</li>
<li>Nk: 密钥包含的 32-bit 字的个数,也就是说 Nk=4,6 或 8;</li>
<li>Nr: 加密的轮数,对于不同密钥长度,轮数不一样,具体如下表所示:</li>
</ul>
<table><thead><tr><th style="text-align: center"></th><th style="text-align: center">密钥长度(Nk words)</th><th style="text-align: center">分组大小(Nb words)</th><th style="text-align: center">轮数(Nr)</th></tr></thead><tbody>
<tr><td style="text-align: center">AES-128</td><td style="text-align: center">4</td><td style="text-align: center">4</td><td style="text-align: center">10</td></tr>
<tr><td style="text-align: center">AES-192</td><td style="text-align: center">6</td><td style="text-align: center">4</td><td style="text-align: center">12</td></tr>
<tr><td style="text-align: center">AES-256</td><td style="text-align: center">8</td><td style="text-align: center">4</td><td style="text-align: center">14</td></tr>
</tbody></table>
<p>下面分三个部分对AES加密解密流程进行梳理</p>
<h2 id="mi-yao-kuo-zhan">密钥扩展</h2>
<p>AES 算法通过密钥扩展程序(Key Expansion)将用户输入的密钥<code>K</code>扩展生成<code>Nb(Nr+1)</code>个字,存放在一个线性数组<code>w[Nb*(Nr+1)]</code>中。具体如下:</p>
<ol>
<li>位置变换函数<code>RotWord()</code>,接受一个字<code>[a0, a1, a2, a3]</code>作为输入,循环左移一个字节后输出<code>[a1, a2, a3, a0]</code>。</li>
<li>S盒变换函数<code>SubWord()</code>,接受一个字<code>[a0, a1, a2, a3]</code>作为输入。S盒是一个16x16的表,其中每一个元素是一个字节。对于输入的每一个字节,前四位组成十六进制数<code>x</code>作为行号,后四位组成的十六进制数<code>y</code>作为列号,查找表中对应的值。最后函数输出 4 个新字节组成的 32-bit 字。</li>
<li>轮常数<code>Rcon[]</code>,如何计算的就不说了,直接把它当做常量数组。
扩展密钥数组<code>w[]</code>的前<code>Nk</code>个元素就是外部密钥<code>K</code>,后的元素<code>w[i]</code>等于它前一个元素<code>w[i-1]</code>与前第<code>Nk</code>个元素<code>w[i-Nk]</code>的异或,即<code>w[i] = w[i-1] XOR w[i-Nk]</code>;但若<code>i</code>为<code>Nk</code>的倍数,则<code>w[i] = w[i-Nk] XOR SubWord(RotWord(w[i-1])) XOR Rcon[i/Nk-1]</code>。</li>
</ol>
<p>注意,上面的第四步说明仅适合于 AES-128 和 AES-192;</p>
<h2 id="jia-mi">加密</h2>
<p>AES加密过程涉及到4种变换:S盒变换,行变换,列变换,与扩展密钥的异或</p>
<h3 id="she-bian-huan-subbytes">S盒变换-SubBytes()</h3>
<p>在密钥扩展部分已经讲过了,S盒是一个 16 行 16 列的表,表中每个元素都是一个字节。S盒变换很简单:函数<code>SubBytes()</code>接受一个 4x4 的字节矩阵作为输入,对其中的每个字节,前四位组成十六进制数 x 作为行号,后四位组成的十六进制数 y 作为列号,查找表中对应的值替换原来位置上的字节。</p>
<p><img src="https://blog.vhcffh.com/2022/python-implements-aes-encryption/SubBytes.png" alt="SubBytes" /></p>
<h3 id="xing-bian-huan-shiftrows">行变换-ShiftRows()</h3>
<p>行变换也很简单,它仅仅是将矩阵的每一行以字节为单位循环移位:第一行不变,第二行左移一位,第三行左移两位,第四行左移三位。如下图所示:</p>
<p><img src="https://blog.vhcffh.com/2022/python-implements-aes-encryption/ShiftRows.png" alt="ShiftRows" /></p>
<h3 id="yu-kuo-zhan-mi-yao-de-yi-huo-addroundkey">与扩展密钥的异或-AddRoundKey()</h3>
<p>扩展密钥只参与了这一步。根据当前加密的轮数,用<code>w[]</code>中的 4 个扩展密钥与矩阵的 4 个列进行按位异或。如下图:</p>
<p><img src="https://blog.vhcffh.com/2022/python-implements-aes-encryption/AddRoundKey.png" alt="AddRoundKey" /></p>
<h2 id="jie-mi">解密</h2>
<p>解密需要分别实现 S 盒变换、行变换和列变换的逆变换InvShiftRows(),InvSubBytes(),InvMixColumns()</p>
<h1 id="pythonshi-xian">python实现</h1>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">S_Box = [
[0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,
0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76],
[0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0],
[0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15],
[0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A,
0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75],
[0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84],
[0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B,
0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF],
[0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85,
0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8],
[0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2],
[0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17,
0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73],
[0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88,
0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB],
[0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79],
[0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9,
0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08],
[0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A],
[0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E],
[0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94,
0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF],
[0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68,
0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]]
Inv_S_Box = [
[0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38,
0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB],
[0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,
0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB],
[0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D,
0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E],
[0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2,
0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25],
[0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16,
0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92],
[0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA,
0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84],
[0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A,
0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06],
[0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02,
0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B],
[0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA,
0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73],
[0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85,
0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E],
[0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89,
0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B],
[0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20,
0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4],
[0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31,
0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F],
[0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D,
0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF],
[0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0,
0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61],
[0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26,
0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D]]
# 轮常数,密钥扩展中用到。(AES-128只需要10轮)
Rcon = [0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000]
# 密钥扩展
# 字循环左移一个字节
def RotWord(x):
return ((x << 8) | (x >> 24)) & 0xffffffff
# S盒变换
def SubWord(x):
temp = 0
temp = (temp << 8) | S_Box[(x >> 28) & 0x0f][(x >> 24) & 0x0f]
temp = (temp << 8) | S_Box[(x >> 20) & 0x0f][(x >> 16) & 0x0f]
temp = (temp << 8) | S_Box[(x >> 12) & 0x0f][(x >> 8) & 0x0f]
temp = (temp << 8) | S_Box[(x >> 4) & 0x0f][(x >> 0) & 0x0f]
return temp
# 加密过程
# 轮密钥加变换 - 将每一列与扩展密钥进行异或
def AddRoundKey(mtx, ikey):
rmtx = []
for i in range(4):
for j in range(4):
rmtx.append((mtx[4 * i + j] ^ ((ikey[j] << 8 * i) >> 24)) & 0xff)
return bytes(rmtx)
# S盒变换 - 前4位为行号,后4位为列号
def Subbytes(mtx):
rmtx = []
for i in mtx:
rmtx.append(S_Box[i >> 4][i & 0x0f])
return bytes(rmtx)
# 行变换 - 按字节循环移位
def ShiftRows(mtx):
rmtx = b''
rmtx += mtx[0:4] + \
mtx[5:8] + mtx[4:5] + \
mtx[10:12] + mtx[8:10] + \
mtx[15:16] + mtx[12:15]
return rmtx
# 有限域上的乘法 GF(2^8)
def GFMul(a, b):
p, hi_bit_set = 0, 0
for counter in range(8):
if b & 1 != 0:
p ^= a
hi_bit_set = a & 0x80
a = (a << 1) & 0xff
if hi_bit_set != 0:
a ^= 0x1b # x^8 + x^4 + x^3 + x + 1
b >>= 1
return p & 0xff
# 列变换
def MixColumns(mtx):
rmtx = [0] * 16
for i in range(4):
arr = []
for j in range(4):
arr.append(mtx[i + j * 4])
rmtx[i] = GFMul(0x02, arr[0]) ^ GFMul(0x03, arr[1]) ^ arr[2] ^ arr[3]
rmtx[i + 4] = arr[0] ^ GFMul(0x02, arr[1]) ^ GFMul(0x03, arr[2]) ^ arr[3]
rmtx[i + 8] = arr[0] ^ arr[1] ^ GFMul(0x02, arr[2]) ^ GFMul(0x03, arr[3])
rmtx[i + 12] = GFMul(0x03, arr[0]) ^ arr[1] ^ arr[2] ^ GFMul(0x02, arr[3])
return bytes(rmtx)
# 解密过程
# 逆行变换 - 按字节循环移位
def InvShiftRows(mtx):
rmtx = b''
rmtx += mtx[0:4] + \
mtx[7:8] + mtx[4:7] + \
mtx[10:12] + mtx[8:10] + \
mtx[13:16] + mtx[12:13]
return rmtx
# 逆S盒变换 - 前4位为行号,后4位为列号
def InvSubbytes(mtx):
rmtx = []
for i in mtx:
rmtx.append(Inv_S_Box[i >> 4][i & 0x0f])
return bytes(rmtx)
# 逆列变换
def InvMixColumns(mtx):
rmtx = [0] * 16
for i in range(4):
arr = []
for j in range(4):
arr.append(mtx[i + j * 4])
rmtx[i] = GFMul(0x0e, arr[0]) ^ GFMul(0x0b, arr[1]) ^ GFMul(0x0d, arr[2]) ^ GFMul(0x09, arr[3])
rmtx[i + 4] = GFMul(0x09, arr[0]) ^ GFMul(0x0e, arr[1]) ^ GFMul(0x0b, arr[2]) ^ GFMul(0x0d, arr[3])
rmtx[i + 8] = GFMul(0x0d, arr[0]) ^ GFMul(0x09, arr[1]) ^ GFMul(0x0e, arr[2]) ^ GFMul(0x0b, arr[3])
rmtx[i + 12] = GFMul(0x0b, arr[0]) ^ GFMul(0x0d, arr[1]) ^ GFMul(0x09, arr[2]) ^ GFMul(0x0e, arr[3])
return bytes(rmtx)
class AES(object):
# AES-128
# Nk密钥长度(双字),Nb分组大小(双字),Nr轮数
# 128 4 4 10
# 192 6 4 12
# 256 8 4 14
# 128 4*4*8=128bits 4*4*8=128bits 10轮
def __init__(self, K: bytes):
self.Nk, self.Nb, self.Nr = 4, 4, 10
# 通过密钥K生成实例,密钥K位数不足补零
self.K = K[:self.Nk * 4]
while len(self.K) < self.Nk * 4:
self.K += b'\x00'
self.W = self.KeyExpansion()
def Encrypt(self, m: bytes) -> bytes:
# 加密
# 明文不足16*8bits补零
while len(m) % 16 != 0:
m += b'\x00'
c = b''
for i in range(len(m) // 16):
# 对每一个16*8bits的块进行循环
mtx = m[i * 16:i * 16 + 16]
ikey = self.W[:4]
mtx = AddRoundKey(mtx, ikey)
for round in range(1, self.Nr):
mtx = Subbytes(mtx)
mtx = ShiftRows(mtx)
mtx = MixColumns(mtx)
ikey = self.W[4*round:4*round+4]
mtx = AddRoundKey(mtx, ikey)
mtx = Subbytes(mtx)
mtx = ShiftRows(mtx)
ikey = self.W[4 * self.Nr:]
mtx = AddRoundKey(mtx, ikey)
c += mtx
return c
def Decrypt(self, m: bytes) -> bytes:
# 解密
c = b''
for i in range(len(m) // 16):
mtx = m[i * 16:i * 16 + 16]
ikey = self.W[self.Nr * 4:]
mtx = AddRoundKey(mtx, ikey)
for round in range(self.Nr-1, 0, -1):
mtx = InvShiftRows(mtx)
mtx = InvSubbytes(mtx)
ikey = self.W[4 * round:4 * round + 4]
mtx = AddRoundKey(mtx, ikey)
mtx = InvMixColumns(mtx)
mtx = InvShiftRows(mtx)
mtx = InvSubbytes(mtx)
ikey = self.W[:4]
mtx = AddRoundKey(mtx, ikey)
c += mtx
return c
def KeyExpansion(self):
# 密钥扩展函数,密钥 K 扩展生成 Nb(Nr+1)个字 4*4*8bits=128bits -> 4*(10+1)*32bits=1408bits
w = []
for i in range(self.Nk):
temp = 0
for j in range(4):
temp = (temp << 8) | self.K[4 * i + j]
w.append(temp)
for i in range(self.Nk, self.Nb * (self.Nr + 1)):
temp = w[i - 1]
if i % self.Nk == 0:
temp = SubWord(RotWord(temp)) ^ Rcon[i // self.Nk - 1]
elif self.Nk > 6 and i % self.Nk == 4:
temp = SubWord(temp)
w.append(w[i - self.Nk] ^ temp)
return w
def getK(self) -> bytes:
# 返回密钥K
return self.K
def generateK(self) -> bytes:
# 随机产生密钥K
pass
def print_bytes_hex(m):
for i in m:
print(hex(i)[2:].rjust(2, '0'), end='')
print()
if __name__ == '__main__':
# AES-128
# 密钥不足128bits 添零,多余128bits 截取前128bits
# messages 不足128bits的倍数 补零
m = b'https://blog.vhcffh.com'
key = b'123456'
a = AES(key)
cc = a.Encrypt(m)
mm = a.Decrypt(cc)
print("明文:", end='')
print(m)
print("密钥:", end='')
print(a.K)
print("密文:", end='')
print_bytes_hex(cc) # 以bytes输出
print("解密:", end='')
print(mm)
</code></pre>
<h1 id="can-kao">参考</h1>
<ol>
<li><a href="https://github.com/FreyZhang007/python-encrypt/blob/93090b37a92005781c01bf21921c632e35f50254/aes.py">aes加密python源码</a></li>
<li><a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">Advanced Encryption Standard</a></li>
<li><a href="https://songlee24.github.io/2014/12/06/des-encrypt/">DES加密算法的C++实现</a></li>
</ol>
python实现DES加密
2022-11-07T00:00:00+00:00
2022-11-07T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/python-implements-des-encryption/
<h1 id="dessuan-fa-yuan-li">DES算法原理</h1>
<p>DES算法是一种最通用的对称密钥算法,因为算法本身是公开的,所以其安全性取决于密钥的安全性。
基于密钥的算法通常有两类:对称算法和公开密钥算法。
对称算法的对称性体现在加密密钥能够从解密密钥推算出来,反之亦然。
在大多数对称算法中,加解密的密钥是相同的,DES就是这样。
可见,对称密钥算法的加解密密钥都是保密的。
而公开密钥算法的密钥有两个(公钥和私钥),公钥是公开的,私钥是保密的。</p>
<p>下面是 DES 加密算法的整体流程图:</p>
<p><img src="https://blog.vhcffh.com/2022/python-implements-des-encryption/des.png" alt="DES" /></p>
<p>从上面的流程图可以看出,DES加密主要由四个部分完成:</p>
<ol>
<li>初始置换 IP</li>
<li>子密钥 Ki 的获取</li>
<li>密码函数 f</li>
<li>尾置换 FP</li>
</ol>
<p>其中,第二部分和第三部分是 DES 算法的核心。
注意:DES 解密算法与加密算法完全相同,只需要将子密钥的使用顺序反过来就行了。</p>
<p>下面针对这四个步骤分别讲一下大致思路。</p>
<h2 id="1-chu-shi-zhi-huan-ip">1.初始置换IP</h2>
<p>这一部分很简单,IP(initial permutation)是一个 8x8 的置换表:</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">IP = (58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7)
</code></pre>
<p>根据表中的规定,将输入的 64 位明文重新进行排序,即将第 58 位放到第 1 位,第 50 位放到第 2 位……以此类推。
初始置换以后得到的是一个 64 位的输出。</p>
<h2 id="2-zi-mi-yao-kide-huo-qu">2.子密钥Ki的获取</h2>
<p>下面是获取子密钥 Ki 的流程图:</p>
<p><img src="https://blog.vhcffh.com/2022/python-implements-des-encryption/./Ki.png" alt="Ki" /></p>
<ul>
<li>用户输出的密钥是 64 位的,根据密钥置换表PC-1,将 64 位变成 56 位密钥。(去掉了奇偶校验位)</li>
<li>将 PC-1 置换得到的 56 位密钥,分为前28位 C0 和后28位 D0,分别对它们进行循环左移,C0 左移得到 C1,D0 左移得到 D1。</li>
<li>将 C1 和 D1 合并成 56 位,然后通过PC-2表进行压缩置换,得到当前这一轮的 48 位子密钥 K1 。</li>
<li>然后对 C1 和 D1 进行左移和压缩置换,获取下一轮的子密钥……一共进行16轮,得到 16 个 48 位的子密钥。</li>
</ul>
<p>PC-1和PC-2如下:</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">PC_1L = (57, 49, 41, 33, 25, 17, 9,
1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27,
19, 11, 3, 60, 52, 44, 36)
PC_1R = (63, 55, 47, 39, 31, 23, 15,
7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29,
21, 13, 5, 28, 20, 12, 4)
PC_2 = (14, 17, 11, 24, 1, 5,
3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8,
16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55,
30, 40, 51, 45, 33, 48,
44, 49, 39, 56, 34, 53,
46, 42, 50, 36, 29, 32)
</code></pre>
<h2 id="3-mi-ma-han-shu-f">3.密码函数f</h2>
<p>下面是密码函数f(R, K)的流程图:</p>
<p><img src="https://blog.vhcffh.com/2022/python-implements-des-encryption/./f.png" alt="f(R,K)" /></p>
<p>密码函数f(R, K)接受两个输入:32 位的数据和 48 位的子密钥。然后:</p>
<ul>
<li>通过表 E 进行扩展置换,将输入的 32 位数据扩展为 48 位</li>
<li>将扩展后的 48 位数据与 48 位的子密钥进行异或运算</li>
<li>将异或得到的 48 位数据分成 8 个 6 位的块,每一个块通过对应的一个 S 表产生一个 4 位的输出。其中,每个 S 表都是 4 行 16 列。具体的置换过程如下:把 6 位输入中的第 1 位和第 6 位取出来行成一个两位的二进制数 x ,作为 Si 表中的行数(0~3);把 6 位输入的中间 4 位构成另外一个二进制数 y,作为 Si 表的列数(0~15);查出 Si 表中 x 行 y 列所对应的整数,将该整数转换为一个 4 位的二进制数</li>
<li>把通过 S 表置换得到的 8 个 4 位连在一起,形成一个 32 位的数据。然后将该 32 位数据通过表 P 进行置换(称为P-置换),置换后得到一个仍然是 32 位的结果数据,这就是f(R, K)函数的输出</li>
</ul>
<p>这部分用到了扩展置换表E,8个S表以及P-置换表:</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">E = (32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32, 1)
P = (16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25)
S = [[[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7],
[0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8],
[4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0],
[15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13]],
[[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10],
[3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],
[0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15],
[13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9]],
[[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8],
[13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],
[13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7],
[1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12]],
[[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15],
[13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],
[10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4],
[3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14]],
[[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9],
[14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],
[4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14],
[11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3]],
[[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11],
[10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],
[9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6],
[4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13]],
[[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1],
[13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],
[1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2],
[6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]],
[[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7],
[1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2],
[7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8],
[2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]]]
</code></pre>
<h2 id="4-wei-zhi-huan-fp">4.尾置换FP</h2>
<p>合并 L16 和 R16 得到一个 64 位的数据,再经过尾置换后得到的就是 64 位的密文。
注意:要将 L16 和 R16 合并成 R16L16(即左右互换)。
尾置换表FP如下:</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">FP = (40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25)
</code></pre>
<h1 id="pythonshi-xian">python实现</h1>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">IP = (58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7)
FP = (40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25)
E = (32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32, 1)
P = (16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25)
S = [[[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7],
[0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8],
[4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0],
[15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13]],
[[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10],
[3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],
[0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15],
[13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9]],
[[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8],
[13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],
[13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7],
[1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12]],
[[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15],
[13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],
[10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4],
[3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14]],
[[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9],
[14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],
[4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14],
[11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3]],
[[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11],
[10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],
[9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6],
[4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13]],
[[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1],
[13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],
[1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2],
[6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]],
[[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7],
[1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2],
[7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8],
[2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]]]
PC_1L = (57, 49, 41, 33, 25, 17, 9,
1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27,
19, 11, 3, 60, 52, 44, 36)
PC_1R = (63, 55, 47, 39, 31, 23, 15,
7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29,
21, 13, 5, 28, 20, 12, 4)
PC_2 = (14, 17, 11, 24, 1, 5,
3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8,
16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55,
30, 40, 51, 45, 33, 48,
44, 49, 39, 56, 34, 53,
46, 42, 50, 36, 29, 32)
def Permute(block, b_len, PP):
# 通过置换矩阵PP对block进行置换,b_len是块长度(位,bit)
res = 0
for i in PP:
res = res << 1
# 第i-1的位置 即倒数第
res |= (block >> (b_len - i)) & 0x01
return res
def bytesToblocks(m):
# 字节序列转换位blocks b'\x00\x01\x02\x03\xff\xff\xff\xff' -> [0x00010203,0xffffffff]
while len(m) % 8 != 0:
m += b'\x00'
blocks = []
for i in range(len(m) // 4):
blocks.append((m[4 * i] << 24) | (m[4 * i + 1] << 16) |
(m[4 * i + 2] << 8) | (m[4 * i + 3]))
return blocks
def blocksTobytes(blocks):
# blocks转换为字节序列 [0x00010203,0xffffffff] -> b'\x00\x01\x02\x03\xff\xff\xff\xff'
res = b''
for i in blocks:
res += i.to_bytes(4, byteorder="big")
return res
L = lambda x, n: ((x << n) | (x >> (28 - n))) & 0x0fffffff
# 对一个28bits的数x进行循环左移n位
class DES(object):
def F(self, block, subKeyid):
"""
:param block: 32bits
:param subKeyid:
:return: res: 32bits
"""
temp = Permute(block, 32, E) ^ self.subKs[subKeyid]
res = 0
for i in range(8):
res = res << 4
yxxxxy = (temp >> 6 * (7 - i)) & 0x3f
xxxx = (yxxxxy & 0x1f) >> 1
yy = ((yxxxxy >> 5) << 1) | (yxxxxy & 0x01)
res |= S[i][yy][xxxx]
res = Permute(res, 32, P)
return res & 0xffffffff
def __init__(self, K: bytes):
# 通过密钥K生成实例
self.K = K
self.K_blocks = bytesToblocks(K)[:2]
# 生成字密钥self.subKs[16]
self.subKs = None
self.generate_subKs()
def Encrypt(self, m: bytes) -> bytes:
# 加密
blocks = bytesToblocks(m)
cblocks = []
for i in range(len(blocks) // 2):
# 每个64bits的高32bits,低32bits
high, low = blocks[2 * i], blocks[2 * i + 1]
# 第一步:初始置换IP
temp = Permute((high << 32) | low, 64, IP) & 0xffffffffffffffff
# 第二步:获取 Li 和 Ri
high, low = temp >> 32, temp & 0xffffffff
# 第三步:共16轮迭代
for j in range(16):
high, low = low, (high ^ self.F(low, j))
# 第四步:合并L16和R16,注意合并为 R16L16
high, low = low, high
# 第五步:末尾置换FP
temp = Permute((high << 32) | low, 64, FP) & 0xffffffffffffffff
high, low = temp >> 32, temp & 0xffffffff
cblocks.append(high)
cblocks.append(low)
return blocksTobytes(cblocks)
def Decrypt(self, e: bytes) -> bytes:
# 解密
blocks = bytesToblocks(e)
cblocks = []
for i in range(len(blocks) // 2):
# 每个64bits的高32bits,低32bits
high, low = blocks[2 * i], blocks[2 * i + 1]
# 第一步:初始置换IP
temp = Permute((high << 32) | low, 64, IP) & 0xffffffffffffffff
# 第二步:获取 Li 和 Ri
high, low = temp >> 32, temp & 0xffffffff
# 第三步:共16轮迭代, 子密钥逆序
for j in range(16):
high, low = low, (high ^ self.F(low, 15 - j))
# 第四步:合并L16和R16,注意合并为 R16L16
high, low = low, high
# 第五步:末尾置换FP
temp = Permute((high << 32) | low, 64, FP) & 0xffffffffffffffff
high, low = temp >> 32, temp & 0xffffffff
cblocks.append(high)
cblocks.append(low)
return blocksTobytes(cblocks)
def generate_subKs(self):
# print(bin((self.K_blocks[0] << 32) | self.K_blocks[1])[2:].rjust(32, '0'))
C = Permute((self.K_blocks[0] << 32) | self.K_blocks[1], 64, PC_1L) & 0x0fffffff
D = Permute((self.K_blocks[0] << 32) | self.K_blocks[1], 64, PC_1R) & 0x0fffffff
# print(bin(C)[2:].rjust(28, '0'))
# print(bin(D)[2:].rjust(28, '0'))
self.subKs = []
for i in range(16):
if i in (0, 1, 8, 15):
C, D = L(C, 1), L(D, 1)
else:
C, D = L(C, 2), L(D, 2)
self.subKs.append(Permute((C << 28) | D, 56, PC_2))
# print(hex(self.subKs[i]))
def getK(self) -> bytes:
# 返回密钥K
return self.K
def generateK(self) -> bytes:
# 随机产生密钥K
pass
def print_bytes_hex(m):
for i in m:
print(hex(i)[2:].rjust(2, '0'), end='')
print()
if __name__ == '__main__':
# 密钥不足64bits 添零,多于64bits 使用前64bits
# messages 不足64bits的倍数 补零
m = b'https://blog.vhcffh.com' # 明文
k = b'123456' # 密钥
a = DES(k)
cc = a.Encrypt(m)
mm = a.Decrypt(cc)
print("明文:", end='')
print(m)
print("密钥:", end='')
print(a.K)
print("密文:", end='')
print_bytes_hex(cc) # 以bytes输出
print("解密:", end='')
print(mm)
</code></pre>
<h1 id="can-kao">参考</h1>
<ol>
<li><a href="https://github.com/FreyZhang007/python-encrypt/blob/93090b37a92005781c01bf21921c632e35f50254/des.py">des加密python源码</a></li>
<li><a href="http://www.hankcs.com/security/des-algorithm-illustrated.html">DES算法实例详解</a></li>
<li><a href="https://songlee24.github.io/2014/12/06/des-encrypt/">DES加密算法的C++实现</a></li>
</ol>
树莓派文件系统只读
2022-10-13T00:00:00+00:00
2022-10-13T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/shu-mei-pai-wen-jian-xi-tong-zhi-du/
<h3 id="wen-ti-yuan-yin">问题原因</h3>
<p>新烧录树莓派镜像后,文件系统变为只读</p>
<h3 id="jie-jue-fang-fa">解决方法</h3>
<p>需要进入单用户模式,使用<code>fsck</code>命令对文件系统进行修复</p>
<p>具体操作步骤</p>
<ol>
<li>进入单用户模式</li>
</ol>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash"># 切换到root用户
sudo su root
# 进入单用户模式
init 1
# 可以通过此命令查看当前系统运行模式
runlevel
</code></pre>
<ol start="2">
<li>使用<code>fsck</code>命令对文件系统进行修复, 主要修复根目录所在分区</li>
</ol>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash"># 查看当前文件硬盘
lsblk
# sda 8:0 0 465.8G 0 disk
# └─sda1 8:1 0 465.8G 0 part /mnt/disk1
# mmcblk0 179:0 0 14.8G 0 disk
# ├─mmcblk0p1 179:1 0 213.6M 0 part /boot
# └─mmcblk0p2 179:2 0 14.6G 0 part /
# 卸载需要修复的分区
umount /dev/mmcblk0p2
# 对文件系统修复,一路确认即可
fsck.ext4 -y /dev/mmcblk0p2
</code></pre>
<ol start="3">
<li>重启系统</li>
</ol>
<p>重启后尽量在重启一次</p>
<p>如果仍然只读,可尝试重新再修复一次</p>
常见Hash算法
2022-10-11T00:00:00+00:00
2022-10-11T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/hash-algorithm/
<h3 id="shi-yao-shi-hashsuan-fa">什么是hash算法</h3>
<p>Hash算法又称散列算法,特点是把任意长度的输出,通过一些列的计算后变成固定长度的输出,这个输出值即为散列值。由于散列值的空间远小于输出空间,因此存在不同输入得到相同输出的情况,这种现象称为碰撞。</p>
<h3 id="hashsuan-fa-de-ying-yong">hash算法的应用</h3>
<p>hash算法应用广泛,主要有七个方面:安全加密、唯一标识、数据校验、散列函数、负载均衡、数据分片、分布式存储。不同的应用利用的hash算法不同的特性,具体的算法实现也需要根据情况设计。比较常见的实现有md5,sha256等。本文通过这两个算法来研究具体的实现。</p>
<h3 id="md5">MD5</h3>
<p>Md5算法将输入的不定长数据分为512bit的块,并对每个块循环调用MD5运算。</p>
<p><img src="https://blog.vhcffh.com/2022/hash-algorithm/./md5.png" alt="md5" /></p>
<p>一个MD5运算由类似的64次循环构成,分成4组16次。F是一个非线性函数;一个函数运算一次。Mi 表示一个 32-bits 的输入数据,Ki 表示一个 32-bits 常数,用来完成每次不同的计算。</p>
<h4 id="md5de-pythonshi-xian">md5的python实现</h4>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">def Md5sum(message: bytes) -> bytes:
# 定义常量,用于初始化128位变量,注意字节顺序,A=0x01234567,这里低值存放低字节,
# 即01 23 45 67,所以运算时A=0x67452301,其他类似。
# 用字符串的形势,是为了和hex函数的输出统一,hex(10)输出为'0xA',注意结果为字符串。
h0 = 0x67452301
h1 = 0xefcdab89
h2 = 0x98badcfe
h3 = 0x10325476
# 定义每轮中循环左移的位数,用元组表示 4*4*4=64
R = (7, 12, 17, 22) * 4 + (5, 9, 14, 20) * 4 + \
(4, 11, 16, 23) * 4 + (6, 10, 15, 21) * 4
# 定义常数K 64
# K[i] = (int(abs(math.sin(i + 1)) * 2 ** 32)) & 0xffffffff
K = (0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8,
0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193,
0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51,
0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905,
0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681,
0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60,
0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244,
0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314,
0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391)
# 定义每轮中用到的函数。L为循环左移,
# 左移之后可能会超过32位,所以要和0xffffffff做与运算,确保结果为32位。
F = lambda x, y, z: ((x & y) | ((~x) & z))
G = lambda x, y, z: ((x & z) | (y & (~z)))
H = lambda x, y, z: (x ^ y ^ z)
I = lambda x, y, z: (y ^ (x | (~z)))
L = lambda x, n: ((x << n) | (x >> (32 - n))) & 0xffffffff
# 小端 0x12,0x34,0x56,0x78 -> 0x78563412
# 将四个8位无符号数转化为一个32位无符号数
W = lambda i4, i3, i2, i1: (i1 << 24) | (i2 << 16) | (i3 << 8) | i4
# 字节翻转 0x12345678 -> 0x78563412 将一个32位无符号数的高位和低位进行对换
reverse = lambda x: (x << 24) & 0xff000000 | (x << 8) & 0x00ff0000 | \
(x >> 8) & 0x0000ff00 | (x >> 24) & 0x000000ff
# 对每一个输入先添加一个'0x80',即'10000000', 即128
ascii_list = list(map(lambda x: x, message))
msg_length = len(ascii_list) * 8
ascii_list.append(128)
# 补充0
while (len(ascii_list) * 8 + 64) % 512 != 0:
ascii_list.append(0)
# 最后64为存放消息长度,以小端数存放。
# 例如,消息为'a',则长度是8,则添加'0x0800000000000000'
for i in range(8):
ascii_list.append((msg_length >> (8 * i)) & 0xff)
# print(ascii_list)
# print(len(ascii_list)//64)
# 对每一消息块进行迭代
for i in range(len(ascii_list) // 64):
# print(ascii_list[i*64:(i+1)*64])
# 对每一个消息块进行循环,每个消息块512bits=16*32bits=64*8bits
a, b, c, d = h0, h1, h2, h3
for j in range(64):
# 64轮的主循环
if 0 <= j <= 15:
f = F(b, c, d) & 0xffffffff
g = j
elif 16 <= j <= 31:
f = G(b, c, d) & 0xffffffff
g = ((5 * j) + 1) % 16
elif 32 <= j <= 47:
f = H(b, c, d) & 0xffffffff
g = ((3 * j) + 5) % 16
else:
f = I(b, c, d) & 0xffffffff
g = (7 * j) % 16
aa, dd, cc = d, c, b
# 第i个chunk,第g个32-bit
s = i * 64 + g * 4
w = W(ascii_list[s], ascii_list[s + 1], ascii_list[s + 2], ascii_list[s + 3])
bb = (L((a + f + K[j] + w) & 0xffffffff, R[j]) + b) & 0xffffffff
a, b, c, d = aa, bb, cc, dd
# print(b)
h0 = (h0 + a) & 0xffffffff
h1 = (h1 + b) & 0xffffffff
h2 = (h2 + c) & 0xffffffff
h3 = (h3 + d) & 0xffffffff
h0, h1, h2, h3 = reverse(h0), reverse(h1), reverse(h2), reverse(h3)
digest = (h0 << 96) | (h1 << 64) | (h2 << 32) | h3
return hex(digest)[2:].rjust(32, '0')
if __name__ == '__main__':
print("自己实现md5(b'https://blog.vhcffh.com')")
print(Md5sum(b"https://blog.vhcffh.com"))
import hashlib
t = hashlib.md5()
t.update(b"https://blog.vhcffh.com")
print("调用hashlib库md5(b'https://blog.vhcffh.com')")
print(t.hexdigest())
</code></pre>
<h4 id="shellming-ling-md5sum">Shell命令md5sum</h4>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">$ echo "blog.vhcffh.com" | md5sum
6f5902ac237024bdd0c176cb93063dc4 -
$ md5sum test.txt
6f5902ac237024bdd0c176cb93063dc4 test.txt
</code></pre>
<h3 id="sha256">SHA256</h3>
<p>SHA256是安全散列算法2(SHA-2,Secure Hash Algorithm 2)中的一个算法标准,由<a href="https://zh.wikipedia.org/wiki/%E7%BE%8E%E5%9B%BD%E5%9B%BD%E5%AE%B6%E6%A0%87%E5%87%86%E4%B8%8E%E6%8A%80%E6%9C%AF%E7%A0%94%E7%A9%B6%E9%99%A2">美国国家标准与技术研究院</a>(NIST)在2001年发布。</p>
<p><img src="https://blog.vhcffh.com/2022/hash-algorithm/./sha256.png" alt="sha256" /></p>
<p>SHA-2的第t个加密循环。图中的深蓝色方块是事先定义好的非线性函数。ABCDEFGH一开始分别是八个初始值,Kt是第t个密钥,Wt是本区块产生第t个word。原消息被切成固定长度的区块,对每一个区块,产生n个word(n视算法而定),透过重复运作循环n次对ABCDEFGH这八个工作区段循环加密。最后一次循环所产生的八段字符串合起来即是此区块对应到的散列字符串。若原消息包含数个区块,则最后还要将这些区块产生的散列字符串加以混合才能产生最后的散列字符串。</p>
<h4 id="sha256de-pythonshi-xian">SHA256的python实现</h4>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">def Sha256sum(message: bytes) -> bytes:
# 定义常量
# 前8个素数2..19的平方根的小数部分的前32位
h0 = 0x6a09e667
h1 = 0xbb67ae85
h2 = 0x3c6ef372
h3 = 0xa54ff53a
h4 = 0x510e527f
h5 = 0x9b05688c
h6 = 0x1f83d9ab
h7 = 0x5be0cd19
# 定义常数K 64
# 前64个素数2..311的立方根的小数部分的前32位
K = (0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2)
# R为循环右移,
# 右移之后可能会超过32位,所以要和0xffffffff做与运算,确保结果为32位。
R = lambda x, n: ((x >> n) | (x << (32 - n))) & 0xffffffff
# 大端 0x12,0x34,0x56,0x78 -> 0x12345678
W = lambda i1, i2, i3, i4: (i1 << 24) | (i2 << 16) | (i3 << 8) | i4
# 对每一个输入先添加一个'0x80',即'10000000', 即128
ascii_list = list(map(lambda x: x, message))
msg_length = len(ascii_list) * 8
ascii_list.append(128)
# 补充0
while (len(ascii_list) * 8 + 64) % 512 != 0:
ascii_list.append(0)
# 最后64为存放消息长度,以大端数存放。
# 例如,消息为'a',则长度为'0x0000000000000008'
for i in range(8):
ascii_list.append(msg_length >> (8 * (7 - i)) & 0xff)
# print(ascii_list)
# print(len(ascii_list)//64)
for i in range(len(ascii_list) // 64): # 64*8=512bits
# print(ascii_list[i*64:(i+1)*64])
# 每个512bits的块进行循环
w = []
# 将512bits扩展到64*32bits=2048bits存入32位无符号数数组
for j in range(16):
s = i * 64 + j * 4
w.append(W(ascii_list[s], ascii_list[s + 1], ascii_list[s + 2], ascii_list[s + 3]))
for j in range(16, 64):
s0 = (R(w[j - 15], 7)) ^ (R(w[j - 15], 18)) ^ (w[j - 15] >> 3)
s1 = (R(w[j - 2], 17)) ^ (R(w[j - 2], 19)) ^ (w[j - 2] >> 10)
w.append((w[j - 16] + s0 + w[j - 7] + s1) & 0xffffffff)
# print(hex(s0)+':'+hex(s1)+':' + hex(R(w[j - 2], 17)))
# 初始化
a, b, c, d, e, f, g, h = h0, h1, h2, h3, h4, h5, h6, h7
# for j in w:
# print(hex(j)[2:])
for j in range(64):
s0 = R(a, 2) ^ R(a, 13) ^ R(a, 22)
maj = (a & b) ^ (a & c) ^ (b & c)
t2 = s0 + maj
s1 = R(e, 6) ^ R(e, 11) ^ R(e, 25)
ch = (e & f) ^ ((~e) & g)
t1 = h + s1 + ch + K[j] + w[j]
h = g & 0xffffffff
g = f & 0xffffffff
f = e & 0xffffffff
e = (d + t1) & 0xffffffff
d = c & 0xffffffff
c = b & 0xffffffff
b = a & 0xffffffff
a = (t1 + t2) & 0xffffffff
h0 = (h0 + a) & 0xffffffff
h1 = (h1 + b) & 0xffffffff
h2 = (h2 + c) & 0xffffffff
h3 = (h3 + d) & 0xffffffff
h4 = (h4 + e) & 0xffffffff
h5 = (h5 + f) & 0xffffffff
h6 = (h6 + g) & 0xffffffff
h7 = (h7 + h) & 0xffffffff
digest = (h0 << 224) | (h1 << 192) | (h2 << 160) | (h3 << 128)
digest |= (h4 << 96) | (h5 << 64) | (h6 << 32) | h7
# print(hex(digest)[2:]) # .rjust(32, '0'))
return hex(digest)[2:] # .rjust(32, '0')
if __name__ == '__main__':
print("自己实现sha256(https://blog.vhcffh.com)")
print(Sha256sum(b"https://blog.vhcffh.com"))
import hashlib
t = hashlib.sha256()
t.update(b"https://blog.vhcffh.com")
print("调用hashlib库sha256(https://blog.vhcffh.com)")
print(t.hexdigest())
</code></pre>
<h4 id="shellming-ling-md5sum-1">Shell命令md5sum</h4>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">$ echo "blog.vhcffh.com" | sha256sum
a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447 -
$ sha256sum test.txt
a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447 test.txt
</code></pre>
<h3 id="can-kao">参考</h3>
<ol>
<li><a href="https://phukety.github.io/2020/11/30/Hash-algorithm/">Hash算法及相关应用 | Phukety的个人博客</a></li>
<li><a href="https://github.com/yangchong211/YCBlogs/blob/master/leetcode/12.Hash/02.%E5%93%88%E5%B8%8C%E7%AE%97%E6%B3%95%E5%BA%94%E7%94%A8.md">YCBlogs/02.哈希算法应用.md</a></li>
</ol>
HashMap Data Structure
2022-08-18T00:00:00+00:00
2022-08-18T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/hashmap-data-structure/
<h3 id="hashmap-data-structure">HashMap Data Structure</h3>
<h4 id="implementation">implementation</h4>
<p>hasmap struct is define:</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">struct bucket
{
void* key;
void* value;
struct bucket *next; // Using linked list to solve hash conflicts
};
struct hashmap {
size_t ( *hash )( struct hashmap*, void* ); // hash function
void ( *key_del )( void* ); // del key
void ( *val_del )( void* ); // del value
int ( *cmp )( void*, void* ); //compare key
size_t size; // hashmap slots
size_t keysize; // sizeof(type key);
struct bucket* buckets; // a struct include key and value
pthread_mutex_t *mutexs; // a mut for every solt
size_t count; // the number of key in hashmap
pthread_mutex_t count_mut; // a mut for count
};
</code></pre>
<p>The specific implementation structure diagram is shown in the figure</p>
<p><img src="https://blog.vhcffh.com/2022/hashmap-data-structure/./structure.png" alt="structure" /></p>
<p>the mutexs array is mainly used to lock each hash solts to realize parallel reading and writing. The array buckets is used to record the head pointer of each hash solts, it does not store data.</p>
<h3 id="performance-characteristics">Performance Characteristics</h3>
<h4 id="implementation-1">implementation</h4>
<p>This hashmap structure uses a linked list to deal with hash conflicts. Initialize a <code>sturct buckets</code> array of size(the number of hashmap solt) to store the head pointer of each solt. When the hash table is empty, its <code>next</code> is NULL. Every time a <key,val> is inserted, a new struct bucket will be initialized in the linked list to store it. When remove, delete the corresponding node.</p>
<h4 id="test">test</h4>
<h5 id="test-hashmap-base"><code>test_hashmap_base</code></h5>
<p>This is a base test for testing basic hashmap functions, insert key, delete key, get key,etc.
Test through several different insert and delete operations</p>
<h5 id="test-concurrent"><code>test_concurrent</code></h5>
<p>Test concurrent modifications
Initialize 5 threads for inserting and 5 threads for reading for testing</p>
<h4 id="research">research</h4>
<p>Hashmap is a solution to improve efficiency by recompressing data. However, because the hash value generated by the hash function is limited and the data may be more, there are still different data corresponding to the same value after processing by the hash function. There are some ways to resolve hash conflicts:</p>
<ol>
<li>Open the address method(Linear detection,Ressquare detection etc.)</li>
<li>Chain address method(linked list)</li>
<li>Establish a public overflow area</li>
<li>Re-hashing
In the hashmap struct, using linked list to resolve hash conflicts.</li>
</ol>
<h4 id="performance">performance</h4>
<ol>
<li>My benchmark is a list. I compared the insertion and query efficiency of hashmap and list in the case of one thread, and tested their efficiency (10e2, 10e3, 10e4, 10e5) under different data volumes.</li>
<li>I wrote a new file performance.c for testing, which implemented hashmap and list tests and calculated their execution time.</li>
</ol>
<p><img src="https://blog.vhcffh.com/2022/hashmap-data-structure/./execution_time.png" alt="execution_time" /></p>
<ol start="3">
<li>The above figure shows the performance comparison between hashmap and ordinary link list. The ordinary link list is relatively fast when inserting, and slower when querying. The speed of hashmap in query is obviously due to the link list. However, due to the need to allocate space when inserting, the speed is slower than the link list.The theories and their time complexity are shown in the following table:</li>
</ol>
<table><thead><tr><th style="text-align: center"></th><th style="text-align: center">time complexity</th></tr></thead><tbody>
<tr><td style="text-align: center">hashmap insert</td><td style="text-align: center">O(1)</td></tr>
<tr><td style="text-align: center">hashmap find</td><td style="text-align: center">O(1)</td></tr>
<tr><td style="text-align: center">link insert</td><td style="text-align: center">O(1)</td></tr>
<tr><td style="text-align: center">link find</td><td style="text-align: center">O(n)</td></tr>
</tbody></table>
<ol start="4">
<li>Compared with ordinary linear lists, hashmap has a huge advantage in query speed.</li>
</ol>
<h3 id="guaranteeing-safe-access">Guaranteeing Safe Access</h3>
<p>For the memory security of key and value, the caller ensures its availability.
For any data in the hashmap, only when the remove_* method is called, unnecessary space will be deleted through key_del and val_del.
And ensure that the space corresponding to the pointer to be returned is not released.
In short, insert transfers the space permissions of key and value to the hashmap structure, and remove_* returns the space permissions of key and value to the caller.</p>
CSS flex 布局
2022-08-16T00:00:00+00:00
2022-08-16T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/css-flex-layout/
<h3 id="flex-direction">flex-direction</h3>
<p>决定主轴方向
<code>row</code>,<code>row-reverse</code>,<code>colum</code>,<code>colum-reverse</code></p>
<h3 id="flex-wrap">flex-wrap</h3>
<p>是否换行
<code>nowrap</code>,<code>wrap</code>,<code>wrap-reverse</code></p>
<h3 id="flex-flow">flex-flow</h3>
<p>flex-direction属性和flex-wrap属性的简写形式</p>
<h3 id="justify-content">justify-content</h3>
<p>对齐方式
<code>flex-start</code>,<code>flex-end</code>,<code>center</code>,<code>space-between</code>,<code>space-around</code></p>
<h3 id="align-items">align-items</h3>
<p>在交叉轴上的对齐方式
<code>flex-start</code>,<code>flex-end</code>,<code>center</code>,<code>baseline</code>,<code>stretch</code></p>
<h3 id="align-content">align-content</h3>
<p>定义多根轴线的对齐方式
<code>flex-start</code>,<code>flex-end</code>,<code>center</code>,<code>space-between</code>,<code>space-around</code>,<code>stretch</code></p>
<h3 id="flex-basis">flex-basis</h3>
<p><code>flex-basis: number|auto|initial|inherit;</code></p>
<p><code>flex-basis</code>设置弹性项目的初始长度。</p>
<table><thead><tr><th>值</th><th>描述</th></tr></thead><tbody>
<tr><td>number</td><td>长度单位或百分百,规定弹性项目的初始长度。</td></tr>
<tr><td>auto</td><td>默认值。长度等于弹性项目的长度。如果该项目未规定长度,则长度将依据其内容。</td></tr>
<tr><td>initial</td><td>将此属性设置为其默认值。</td></tr>
<tr><td>inherit</td><td>从其父元素继承此属性。</td></tr>
</tbody></table>
<h3 id="flex-grow">flex-grow</h3>
<p><code>flex-grow: number|initial|inherit;</code>
<code>flex-grow</code>属性规定在相同的容器中,项目相对于其余弹性项目的增长量。</p>
<table><thead><tr><th>值</th><th>描述</th></tr></thead><tbody>
<tr><td>number</td><td>一个数字,规定项目相对于其他灵活的项目进行扩展的量。默认值是 0。</td></tr>
<tr><td>initial</td><td>将此属性设置为其默认值。</td></tr>
<tr><td>inherit</td><td>从其父元素继承此属性。</td></tr>
</tbody></table>
<h3 id="flex-shrink">flex-shrink</h3>
<p><code>flex-shrink: number|initial|inherit;</code>
<code>flex-shrink</code>属性规定当项目溢出时,按比例对所有项目进行空间收缩。</p>
<table><thead><tr><th>值</th><th>描述</th></tr></thead><tbody>
<tr><td>number</td><td>一个数字,规定项目相对于其他灵活的项目进行扩展的量。默认值是 0。</td></tr>
<tr><td>initial</td><td>将此属性设置为其默认值。</td></tr>
<tr><td>inherit</td><td>从其父元素继承此属性。</td></tr>
</tbody></table>
<h3 id="can-kao">参考</h3>
<p>1.<a href="https://www.runoob.com/w3cnote/flex-grammar.html">Flex布局语法教程|菜鸟教程</a></p>
动态库与静态库
2022-08-10T00:00:00+00:00
2022-08-10T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/static-and-dynamic-libraries/
<h3 id="lian-jie-ku">链接库</h3>
<p>链接库的行为是代码依赖管理的一种形式。
当任何应用程序运行时,其可执行代码都会加载到内存中。
此外,它所依赖的任何代码库也会加载到内存中。
有两种类型的链接:静态的和动态的。
两者都为开发人员提供了不同的好处,应该根据这些好处来选择合适的链接方式。
这篇博文将介绍每种方法提供的好处,然后解释如何在Linux上创建和链接您自己的库的基础知识。</p>
<hr />
<h3 id="dong-tai-lian-jie">动态链接</h3>
<p>链接动态库时,没有任何库代码直接包含在链接目标中。
相反,在解析符号之前,这些库会在运行时加载到内存中。
因为代码不是静态链接到可执行二进制文件中的,所以在运行时加载有一些好处。
主要是,可以使用新功能或错误修复来更新库,而无需重新编译和重新链接可执行文件。
此外,在运行时加载意味着各个代码库可以拥有自己的初始化程序,并在从内存中卸载之前在自己的任务之后进行清理。</p>
<h4 id="dong-tai-ku">动态库</h4>
<p>动态库是一种 Mach-O 二进制文件,在应用程序启动或运行时加载。
由于动态库中的可执行代码不是静态链接到目标可执行文件,因此在需要重用相同代码时提供了一些好处。
例如,如果您有一个应用程序和一个守护程序或扩展程序需要使用相同的代码,那么该代码只需要存在于一个位置——动态库中,而不是同时存在于可执行文件的二进制文件和守护程序的二进制文件中。
由于动态库是在运行时加载的,因此库负责告诉链接器需要哪些附加代码。
这消除了管理您使用的所有代码需要操作的负担。
接下来通过一个简单的例子演示动态库的使用。</p>
<h4 id="gou-jian">构建</h4>
<p>首先构建一个libmax.so的动态库。</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">// max.c
#include "max.h"
int max( int a, int b ) {
if ( a > b ) {
return a;
} else {
return b;
}
}
</code></pre>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">// max.h
#ifndef _MAX_H_
#define _MAX_H_
int max( int a, int b );
#endif
</code></pre>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">// main.h
#include "max.h"
#include <stdio.h>
int main() {
int a = 0, b = 1;
printf("a: %d, b: %d max: %d\n", a, b, max(a,b));
return 1;
}
</code></pre>
<h5 id="bian-yi">编译</h5>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash"># -fPIC是编译选项,PIC是 Position Independent Code 的缩写,表示要生成位置无关的代码,这是动态库需要的特性
gcc -o max.o -c max.c -fPIC
gcc -o main.o -c main.c
</code></pre>
<h5 id="sheng-cheng-dong-tai-ku">生成动态库</h5>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash"># -shared是链接选项,告诉gcc生成动态库而不是可执行文件
gcc -o libmax.so max.o -shared
</code></pre>
<h4 id="lian-jie">链接</h4>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">gcc -o main main.o -L . -lmax
</code></pre>
<h4 id="yun-xing">运行</h4>
<pre><code>$ ./main
./main: error while loading shared libraries: libmax.so: cannot open shared object file: No such file or directory
$ ldd ./main
linux-vdso.so.1 (0x00007fff00bda000)
libmax.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efe58c48000)
/lib64/ld-linux-x86-64.so.2 (0x00007efe58e21000)
</code></pre>
<p>直接运行会出错,因为动态库需要在运行是加载,需要一些配置指定动态库的位置。
通过ldd命令可以查看到系统确实libmax.so的动态库。
有两种方法可以运行我们的程序</p>
<ol>
<li>添加环境变量<code>LD_LIBRARY_PATH</code><pre><code>$ LD_LIBRARY_PATH=. ./main
a: 0, b: 1 max: 1
</code></pre>
</li>
<li>将libmax.so安装到系统,并通过ldconfig更新动态库,可以通过makefile文件编写安装和卸载规则。<br />
当然<code>install</code>和<code>uninstall</code>需要root权限<pre data-lang="makefile" class="language-makefile "><code class="language-makefile" data-lang="makefile">OUTPUT:=./build
LIBDIR:=${OUTPUT}/lib
BINDIR:=${OUTPUT}/bin
main: main.o libmax.so
@mkdir -p ${BINDIR}
gcc -o ${BINDIR}/main ${OUTPUT}/main.o -L ${LIBDIR} -lmax
main.o: main.c
@mkdir -p ${OUTPUT}
gcc -o ${OUTPUT}/main.o -c main.c
max.o: max.c
@mkdir -p ${OUTPUT}
@# -fPIC是编译选项,PIC是 Position Independent Code 的缩写,表示要生成位置无关的代码,这是动态库需要的特性
gcc -o ${OUTPUT}/max.o -c max.c -fPIC
libmax.so: max.o
@mkdir -p ${LIBDIR}
@# -shared是链接选项,告诉gcc生成动态库而不是可执行文件
gcc -o ${LIBDIR}/libmax.so ${OUTPUT}/max.o -shared
install: main
@# 安装文件
cp ${LIBDIR}/libmax.so /usr/lib
cp ${BINDIR}/main /usr/bin
@# ldconfg更新/etc/ld.so.cache
ldconfig
uninstall:
rm -rf /usr/lib/libmax.so
rm -rf /usr/bin/main
ldconfig
clean:
rm -rf build
</code></pre>
</li>
</ol>
<h4 id="yun-xing-shi-jia-zai">运行时加载</h4>
<p>还有一种可以直接在程序运行时指定动态库的路径,并通过系统api直接加载,这样的好处是程序可以只更新部分组件。具体的使用方法参考<a href="https://linux.die.net/man/3/dlopen">dlopen(3) - Linux man page</a>。</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
</code></pre>
<hr />
<h3 id="jing-tai-lian-jie">静态链接</h3>
<p>与动态链接不同,静态链接将库中的代码包含到目标的二进制文件中。
这会导致目标程序占用磁盘空间变大,启动时间变慢。
因为库的代码直接添加到链接目标的二进制文件中,这意味着要更新库中的任何代码,都必须重建链接目标。</p>
<h4 id="jing-tai-ku">静态库</h4>
<p>静态库是一组目标文件的容器。
静态库使用来自<code>ar</code>进行构建,类型的文件扩展名“.a”。
接下来通过一个简单的例子演示静态库的使用。</p>
<h4 id="gou-jian-1">构建</h4>
<p>这里构建一个libmax.a的动态库。
(相关代码与动态库例子相同。)</p>
<h5 id="bian-yi-1">编译</h5>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">gcc -o max.o -c max.c
gcc -o main.o -c main.c
</code></pre>
<h5 id="sheng-cheng-jing-tai-ku">生成静态库</h5>
<p>静态库的生成使用<code>ar</code>工具,具体的使用可以参考<a href="https://linux.die.net/man/1/ar">linux man</a></p>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">ar cr libmax.a max.o
</code></pre>
<h4 id="lian-jie-1">链接</h4>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">gcc -o main main.o -L . -lmax
</code></pre>
<h4 id="yun-xing-1">运行</h4>
<p>直接运行即可,可以比较一下动态库和静态库生成的两个程序的大小,显然动态库生成的程序更小</p>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash"># 静态链接生成的程序
$ ls -all ./main
-rwxr-xr-x 1 frey frey 16664 Aug 10 20:54 ./main
# 动态链接生成的程序
$ ls -all ./main
-rwxr-xr-x 1 frey frey 16640 Aug 11 22:54 ./main
</code></pre>
<h3 id="can-kao">参考</h3>
<ol>
<li><a href="https://pewpewthespells.com/blog/static_and_dynamic_libraries.html">Static and Dynamic Libraries</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/307640255">C语言丨静态库与动态库的区别,你知道多少?</a></li>
<li><a href="https://www.cnblogs.com/jiqingwu/p/linux_dynamic_lib_create.html">Linux动态库生成与使用指南</a></li>
<li><a href="https://linux.die.net/man/3/dlopen">dlopen(3) - Linux man page</a></li>
<li><a href="https://linux.die.net/man/1/ar">ar(1) - Linux man page</a></li>
</ol>
日语中的日期
2022-08-05T00:00:00+00:00
2022-08-05T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/ri-yu-zhong-de-ri-qi/
<h3 id="100yi-xia-de-shu-zi">100以下的数字</h3>
<table><thead><tr><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td>0</td><td>れい、ぜろ</td><td>10</td><td>じゅう</td><td></td><td></td></tr>
<tr><td>1</td><td>いち</td><td>11</td><td>じゅういち</td><td></td><td></td></tr>
<tr><td>2</td><td>に</td><td>12</td><td>じゅうに</td><td>20</td><td>にじゅう</td></tr>
<tr><td>3</td><td>さん</td><td>13</td><td>じゅうさん</td><td>30</td><td>さんじゅう</td></tr>
<tr><td>4</td><td>し、よん</td><td>14</td><td>じゅうし、じゅうよん</td><td>40</td><td>よんじゅう</td></tr>
<tr><td>5</td><td>ご</td><td>15</td><td>じゅうご</td><td>50</td><td>ごじゅう</td></tr>
<tr><td>6</td><td>ろく</td><td>16</td><td>じゅうろく</td><td>60</td><td>ろくじゅう</td></tr>
<tr><td>7</td><td>しち、なな</td><td>17</td><td>じゅうしち、じゅうなな</td><td>70</td><td>ななじゅう</td></tr>
<tr><td>8</td><td>はち</td><td>18</td><td>じゅうはち</td><td>80</td><td>はちじゅう</td></tr>
<tr><td>9</td><td>く、きゅう</td><td>19</td><td>じゅうく、じゅうきゅう</td><td>90</td><td>きゅうじゅう</td></tr>
</tbody></table>
<h3 id="100yi-shang-de-shu-zi">100以上的数字</h3>
<table><thead><tr><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td>100</td><td>ひゃく</td><td>1000</td><td>せん</td><td>10000</td><td>いちまん</td></tr>
<tr><td>200</td><td>にひゃく</td><td>2000</td><td>にせん</td><td>100000</td><td>じゅうまん</td></tr>
<tr><td>300</td><td>さんびゃく</td><td>3000</td><td>さんぜん</td><td>1000000</td><td>ひゃくまん</td></tr>
<tr><td>400</td><td>よんひゃく</td><td>4000</td><td>よんせん</td><td>10000000</td><td>いっせんまん</td></tr>
<tr><td>500</td><td>ごひゃく</td><td>5000</td><td>ごせん</td><td>100000000</td><td>いちおく</td></tr>
<tr><td>600</td><td>ろっぴゃく</td><td>6000</td><td>ろくせん</td><td>9002</td><td>きゅうせんに</td></tr>
<tr><td>700</td><td>ななひゃく</td><td>7000</td><td>ななせん</td><td>9020</td><td>きゅうせんにじゅう</td></tr>
<tr><td>800</td><td>はっぴゃく</td><td>8000</td><td>はっせん</td><td>9200</td><td>きゅうせんにひゃく</td></tr>
<tr><td>900</td><td>きゅうひゃく</td><td>9000</td><td>きゅうせん</td><td></td><td></td></tr>
</tbody></table>
<h3 id="shi">时</h3>
<table><thead><tr><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td>一時</td><td>いちじ</td><td>二時</td><td>にじ</td><td>三時</td><td>さんじ</td><td>四時</td><td>よじ</td></tr>
<tr><td>五時</td><td>ごじ</td><td>六時</td><td>ろくじ</td><td>七時</td><td>しちじ</td><td>八時</td><td>はちじ</td></tr>
<tr><td>九時</td><td>くじ</td><td>十時</td><td>じゅうじ</td><td>十一時</td><td>じゅういちじ</td><td>十二時</td><td>じゅうにじ</td></tr>
<tr><td>〇時</td><td>れいじ</td><td>何時</td><td>なんじ</td><td></td><td></td><td></td><td></td></tr>
</tbody></table>
<h3 id="fen">分</h3>
<table><thead><tr><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td>1分</td><td>いっぷん</td><td>4分</td><td>よんぷん</td><td>7分</td><td>ななふん</td><td>10分</td><td>じゅっぷん</td></tr>
<tr><td>2分</td><td>にふん</td><td>5分</td><td>ごふん</td><td>8分</td><td>はっぷん</td><td>11分</td><td>じゅういっぷん</td></tr>
<tr><td>3分</td><td>さんぷん</td><td>6分</td><td>ろっぷん</td><td>9分</td><td>きゅうふん</td><td>15分</td><td>じゅうごふん</td></tr>
<tr><td>30分</td><td>さんじゅっぷん、はん</td><td>45分</td><td>よんじゅうごふん</td><td>何分</td><td>なんぷん</td><td></td><td></td></tr>
</tbody></table>
<h3 id="xing-qi">星期</h3>
<table><thead><tr><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td>日曜日</td><td>月曜日</td><td>火曜日</td><td>水曜日</td><td>木曜日</td><td>金曜日</td><td>土曜日</td></tr>
<tr><td>にちようび</td><td>げつようび</td><td>かようび</td><td>すいようび</td><td>もくようび</td><td>きんようび</td><td>どようび</td></tr>
<tr><td>星期日</td><td>星期一</td><td>星期二</td><td>星期三</td><td>星期四</td><td>星期五</td><td>星期六</td></tr>
</tbody></table>
<h3 id="ri-qi">日期</h3>
<table><thead><tr><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td>1日</td><td>ついたち</td><td>2日</td><td>ふつか</td><td>3日</td><td>みっか</td><td>4日</td><td>よっか</td></tr>
<tr><td>6日</td><td>むいか</td><td>7日</td><td>なのか</td><td>8日</td><td>ようか</td><td>9日</td><td>ここのか</td></tr>
<tr><td>11日</td><td>じゅういちにち</td><td>12日</td><td>じゅうににち</td><td>13日</td><td>じゅうさんにち</td><td>14日</td><td>じゅうよっか</td></tr>
<tr><td>16日</td><td>じゅうろくにち</td><td>17日</td><td>じゅうしちにち</td><td>18日</td><td>じゅうはちにち</td><td>19日</td><td>じゅうくにち</td></tr>
<tr><td>21日</td><td>にじゅういちにち</td><td>22日</td><td>にじゅうににち</td><td>23日</td><td>にじゅうさんにち</td><td>24日</td><td>にじゅうよっか</td></tr>
<tr><td>26日</td><td>にじゅうろくにち</td><td>27日</td><td>にじゅうしちにち</td><td>28日</td><td>にじゅうはちにち</td><td>29日</td><td>にじゅうくにち</td></tr>
<tr><td>31日</td><td>さんじゅういちにち</td><td>何日</td><td>なんにち</td><td></td><td></td><td></td><td></td></tr>
</tbody></table>
<h3 id="yue-fen">月份</h3>
<table><thead><tr><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td>一月</td><td>いちがつ</td><td>二月</td><td>にがつ</td><td>三月</td><td>さんがつ</td></tr>
<tr><td>四月</td><td>しがつ</td><td>五月</td><td>ごがつ</td><td>六月</td><td>ろくがつ</td></tr>
<tr><td>七月</td><td>しちがつ</td><td>八月</td><td>はちがつ</td><td>九月</td><td>くがつ</td></tr>
<tr><td>十月</td><td>じゅうがつ</td><td>十一月</td><td>じゅういちがつ</td><td>十二月</td><td>じゅうにがつ</td></tr>
<tr><td>何月</td><td>なんがつ</td><td></td><td></td><td></td><td></td></tr>
</tbody></table>
<h3 id="can-kao">参考</h3>
<ol>
<li><a href="https://zh.m.wikipedia.org/zh-cn/%E6%97%A5%E8%AA%9E%E6%95%B8%E5%AD%97">日语数字 - 维基百科,自由的百科全书</a></li>
</ol>
WSL转移后无法入网
2022-08-02T00:00:00+00:00
2022-08-02T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wslzhuan-yi-hou-wu-fa-ru-wang/
<h1 id="yuan-you">缘由</h1>
<p>新组装了一台台式机,需要将笔记本中的WSL子系统导出到新的台式机中,在转移过程中遇到了子系统DNS解析不正常和子系统与Host端口映射问题,在此进行一些记录。</p>
<h1 id="xi-tong-dao-chu">系统导出</h1>
<pre data-lang="powershell" class="language-powershell "><code class="language-powershell" data-lang="powershell"># wsl --export <发行版> <FileName> [选项]
wsl --export Linux linux.tar
</code></pre>
<h1 id="xi-tong-dao-ru">系统导入</h1>
<p>导入系统时,在新系统上执行<code>wsl --help</code>命令后没有<code>--import</code>选项;查找原因后发现需要wsl进行升级,安装<a href="https://docs.microsoft.com/zh-cn/windows/wsl/basic-commands#set-wsl-version-to-1-or-2">教程</a>对wsl进行升级;旧版wsl可能需要进行手动安装<a href="https://docs.microsoft.com/zh-cn/windows/wsl/install-manual">教程</a>。更新后导入即可</p>
<pre data-lang="powershell" class="language-powershell "><code class="language-powershell" data-lang="powershell"># wsl --import <发行版> <InstallLocation> <FileName> [选项]
wsl --import Linux d:/Linux/ linux.tar
</code></pre>
<p>尽量按照到非系统盘,保留系统盘空间</p>
<h1 id="wang-luo-zhong-zhi">网络重置</h1>
<p>导入系统后,系统无法联网,首先查看子系统机host的ip地址情况</p>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.23.202.215 netmask 255.255.240.0 broadcast 172.23.207.255
inet6 fe80::215:5dff:feb6:259d prefixlen 64 scopeid 0x20<link>
ether 00:15:5d:b6:25:9d txqueuelen 1000 (Ethernet)
RX packets 19 bytes 4618 (4.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 30 bytes 1692 (1.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 18 bytes 1546 (1.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 18 bytes 1546 (1.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
$ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.23.192.1 0.0.0.0 UG 0 0 0 eth0
172.23.192.0 0.0.0.0 255.255.240.0 U 0 0 0 eth0
</code></pre>
<pre data-lang="cmd" class="language-cmd "><code class="language-cmd" data-lang="cmd">ipconfig
以太网适配器 vEthernet (WSL):
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::6dfb:1789:6e91:f5dd%48
自动配置 IPv4 地址 . . . . . . . : 169.254.245.221
子网掩码 . . . . . . . . . . . . : 255.255.0.0
默认网关. . . . . . . . . . . . . :
</code></pre>
<p>主机WSL虚拟网卡地址与wsl子系统地址不在一个网段,需要重置主机网络</p>
<pre data-lang="cmd" class="language-cmd "><code class="language-cmd" data-lang="cmd">wsl --shutdown
netsh winsock reset
netsh int ip reset all
netsh winhttp reset proxy
ipconfig /flushdns
</code></pre>
<h1 id="fang-huo-qiang-pei-zhi">防火墙配置</h1>
<p>经过网络重置后,子系统应该可以ping通主机ip,如果无法ping通,可能是windows防火墙配置问题,对windows防火墙添加一条规则</p>
<pre data-lang="powershell" class="language-powershell "><code class="language-powershell" data-lang="powershell">New-NetFirewallRule -DisplayName "WSL" -Direction Inbound -InterfaceAlias "vEthernet (WSL)" -Action Allow
</code></pre>
<h1 id="dnsjie-xi-wen-ti">DNS解析问题</h1>
<p>如果仅仅可以ping通ip,而不能ping通域名,那是域名解析出现了问题
应该是在导入后域名解析出现了问题,一般是由于文件'/etc/resolv.conf'引起的,有两种方式可以处理</p>
<ol>
<li>配置此文件自动生成</li>
</ol>
<p>在文件<code>/etc/wsl.conf</code>中注释掉下面一行,这样在wsl重启后会自动生成文件<code>/etc/resolv.conf</code></p>
<pre><code>[network]
#generateResolvConf = false
</code></pre>
<ol start="2">
<li><a href="https://blog.vhcffh.com/wp-675/">自己创建此文件</a></li>
</ol>
<p>建议用这种方式,可以在子系统中自定义域名解析服务器</p>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">sudo rm /etc/resolv.conf
sudo bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf'
sudo bash -c 'echo "[network]" > /etc/wsl.conf'
sudo bash -c 'echo "generateResolvConf = false" >> /etc/wsl.conf'
sudo chattr +i /etc/resolv.conf
</code></pre>
<p>首先删除文件,然后新建此文件并重新配置wsl.conf文件,最后chattr命令是更改此文件属性(没有这一句wsl重启后resolv.conf文件会消失)</p>
<h1 id="duan-kou-zhi-jie-ying-she-wen-ti">端口直接映射问题</h1>
<p>在原本的笔记本中,子系统开启的端口(不论是127.0.0.1还是0.0.0.0)都可以在windows中的127.0.0.1直接访问,但在新系统中无法实现</p>
<p>经排查是进程wslhost.exe的问题,主要是window系统版本不同引起的
在windows更新中加入预览版体验,跟新系统版本到22H2后wsl的端口直接映射功能就有了,而且增加了子系统的窗口组件,可以直接运行子系统的窗口应用了
并且要更新wsl</p>
<pre data-lang="cmd" class="language-cmd "><code class="language-cmd" data-lang="cmd">wsl --update
</code></pre>
<p>但经测试wsl中端口大于2610的仍无法直接映射,原因不明</p>
<h1 id="can-kao">参考</h1>
<ol>
<li><a href="https://docs.microsoft.com/zh-cn/windows/wsl/basic-commands">WSL基本命令</a> </li>
<li><a href="https://docs.microsoft.com/zh-cn/windows/wsl/install-manual">旧版 WSL 的手动安装步骤</a></li>
<li><a href="https://blog.vhcffh.com/wp-675/">WSL2联网问题</a></li>
</ol>
一些不常用C语言关键字
2022-05-21T00:00:00+00:00
2022-05-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-765/
<h2 id="volatile">volatile</h2>
<p>该关键字是一个类型修饰符,提醒编译器它后面所定义的变量随时有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据</p>
<h2 id="typedef">typedef</h2>
<p><code>typedef int myint;</code></p>
<p>声明类型:<code>int a;</code>等价于<code>myint a;</code></p>
<p><code>typedef void (*fun)(void);</code></p>
<p>声明函数指针:<code>void (*p)(void);</code>等价于<code>fun p;</code></p>
<h2 id="register">register</h2>
<p>register将数据存储在寄存器中</p>
<p><code>register type var_name __asm(reg)</code></p>
<p>声明一个命名寄存器的变量,reg为寄存器名</p>
<h2 id="attribute">attribute</h2>
<p><code>__attribute__ ((attribute-list))</code></p>
<p>编译器优化配置</p>
<p>设置函数属性,类型属性,变量属性</p>
<h2 id="inline">inline</h2>
<p>将被调用函数以内联的方式嵌入到调用函数内</p>
树莓派4B运行docker错误
2022-04-12T00:00:00+00:00
2022-04-12T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-760/
<p>树莓派4B安装raspberrypi系统后,用自带的源安装docker,在涉及到libseccomp这个库时会报错</p>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">-------------------------------------
_ ()
| | ___ _ __
| | / __| | | / \
| | \__ \ | | | () |
|_| |___/ |_| \__/
Brought to you by linuxserver.io
-------------------------------------
To support LSIO projects visit:
https://www.linuxserver.io/donate/
-------------------------------------
GID/UID
-------------------------------------
User uid: 1000
User gid: 1000
-------------------------------------
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Your DockerHost is running an outdated version of libseccomp
To fix this, please visit https://docs.linuxserver.io/faq#libseccomp
Apps will not behave correctly without this
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
</code></pre>
<p>具体原因是由于libseccomp2库的一些bug,已经修复但没有被推送到所有仓库,在<a href="https://docs.linuxserver.io/faq#libseccomp">这里</a>有一些解释。也介绍了一些措施去修复。</p>
<p>尝试后并没有用,可能是某些步骤错了。</p>
<p>最后通过通过docker官方脚本重新安装解决了</p>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">sudo apt remove docker.io
sudo apt autoremove
sudo apt update
sudo apt upgrade
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
systemctl start docker
</code></pre>
<p>docker官方下载的安装脚本<code>get-docker.sh</code>会给apt添加一个新的源,并从docker的官方源重新安装docker的最新版本。</p>
<h1 id="can-kao">参考</h1>
<ol>
<li><a href="https://docs.linuxserver.io/faq#libseccomp">FAQ - LinuxServer.io</a></li>
</ol>
树莓派4B wlan0消失
2022-04-10T00:00:00+00:00
2022-04-10T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-756/
<ol>
<li>wlan0 没有启动,使用<code>sudo ifconfig wlan0 up</code>启用即可</li>
<li>wlan0被blocked,使用<code>sudo rfkill unblock all</code>解锁即可</li>
</ol>
Yew框架实现秒表
2022-04-03T00:00:00+00:00
2022-04-03T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-721/
<h2 id="shi-xian-ji-shu-qi">实现计数器</h2>
<p>用Yew框架实现一个web定时器,首先Interval实现一个简单的计数器,每10ms发送Msg
UpdateTime并组件的update方法中调用tick方法,更新时间。</p>
<pre data-lang="rust" class="language-rust "><code class="language-rust" data-lang="rust">use gloo::timers::callback::Interval;
use yew::{html, Component, Context, Html};
pub enum Msg {
UpdateTime,
}
pub struct MyTimer {
timer_min: u32,
timer_sec: u32,
timer_csec: u32,
timer_handle: Option<Interval>,
}
impl MyTimer {
fn tick(&mut self) {
self.timer_csec += 1;
if self.timer_csec >= 100{
self.timer_csec = 0;
self.timer_sec += 1;
if self.timer_sec == 60 {
self.timer_sec = 0;
self.timer_min += 1;
}
}
}
}
impl Component for MyTimer {
type Message = Msg;
type Properties = ();
fn create(ctx: &Context<Self>) -> Self {
let timer_handle = {
let link = ctx.link().clone();
Interval::new(10, move || link.send_message(Msg::UpdateTime))
};
let timer_handle = Some(timer_handle);
Self {
timer_min: 0,
timer_sec: 0,
timer_csec: 0,
timer_handle,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::UpdateTime => {
self.tick();
true
}
}
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
<>
<div id="time">
{ format!("{:0>2}:{:0>2}:{:0>2}",&self.timer_min,&self.timer_sec,&self.timer_csec) }
</div>
</>
}
}
}
</code></pre>
<h2 id="shi-xian-miao-biao-gong-neng">实现秒表功能</h2>
<p>秒表需要实现<strong>开始</strong>、<strong>停止</strong>、<strong>继续</strong>、<strong>计次</strong>、<strong>重置</strong>五个功能。因为开始和继续都是初始化定时器,因此可以用同一个Msg表示,在<code>Msg</code>中添加这4个消息。</p>
<pre data-lang="rust" class="language-rust "><code class="language-rust" data-lang="rust">pub enum Msg {
StartTime,
StopTime,
CountTime,
ResetTime,
UpdateTime,
}
</code></pre>
<h3 id="kai-shi">开始</h3>
<p>默认秒表应该处于停止状态,点击开始按钮,发送<code>StartTime</code>消息后,开始计数。因此需要将<code>Interval</code>的初始化放到<code>update</code>方法中。在<code>create</code>方法中用<code>None</code>初始化<code>timer_handle</code>,在<code>update</code>中接收到<code>StartTime</code>消息后进行初始化。</p>
<pre data-lang="rust" class="language-rust "><code class="language-rust" data-lang="rust"> match msg {
Msg::StartTime => {
let timer_handle = {
let link = ctx.link().clone();
Interval::new(10, move || link.send_message(Msg::UpdateTime))
};
self.timer_handle = Some(timer_handle);
true
}
// ...
}
</code></pre>
<h3 id="ting-zhi">停止</h3>
<p>停止秒表直接将timer_handle重新赋值为None即可,这样就删除了定时器。</p>
<pre data-lang="rust" class="language-rust "><code class="language-rust" data-lang="rust"> Msg::StopTime=>{
self.timer_handle = None;
true
}
</code></pre>
<h3 id="ji-xu">继续</h3>
<p>继续与开始的逻辑相同。</p>
<h3 id="ji-ci">计次</h3>
<p>计次需要一个变量来保存当前的时间,因此在结构体中增加一个可变数组用于保存当前时间。</p>
<pre data-lang="rust" class="language-rust "><code class="language-rust" data-lang="rust"> Msg::CountTime => {
self.message.push(
format!("{:0>2}:{:0>2}:{:0>2}",&self.timer_min,&self.timer_sec,&self.timer_csec) }
);
true
}
</code></pre>
<h3 id="zhong-zhi">重置</h3>
<p>重置需要将所有计时器归零,并清空message中的计数。</p>
<pre data-lang="rust" class="language-rust "><code class="language-rust" data-lang="rust"> Msg::ResetTime=>{
self.timer_csec = 0;
self.timer_sec = 0;
self.timer_min = 0;
self.message.clear();
true
}
</code></pre>
<p>到这里,秒表的基本功基本完全实现了,接下来将update方法中的代码整理到结构体为结构体的方法,添加一些变量来记录秒表的状态,另外添加一些css来优化界面。</p>
<h2 id="dai-ma-zheng-li">代码整理</h2>
<p>将代码逻辑进行一些整理优化</p>
<pre data-lang="rust" class="language-rust "><code class="language-rust" data-lang="rust">use gloo::timers::callback::Interval;
use yew::{html, Component, Context, Html};
pub enum Msg {
StartTime,
StopTime,
CountTime,
ResetTime,
UpdateTime,
}
pub struct MyTimer {
timer_csec: u32,
last_timer_csec: u32,
timer_handle: Option<Interval>,
message: Vec<(u32, u32)>,
state: u8,
}
impl MyTimer {
fn new(_ctx: &Context<Self>) -> Self {
Self {
timer_csec: 0,
last_timer_csec: 0,
timer_handle: None,
message: vec![],
state: 0,
}
}
fn start_time(&mut self, ctx: &Context<Self>) -> bool {
if self.state == 0 || self.state == 2 {
let timer_handle = {
let link = ctx.link().clone();
Interval::new(10, move || link.send_message(Msg::UpdateTime))
};
self.timer_handle = Some(timer_handle);
self.state = 1;
}
true
}
fn stop_time(&mut self) -> bool {
if self.state == 1 {
self.timer_handle = None;
self.state = 2;
}
true
}
fn conut_time(&mut self) -> bool {
if self.state == 1 {
self.message
.push((self.timer_csec, self.timer_csec - self.last_timer_csec));
self.last_timer_csec = self.timer_csec;
}
true
}
fn reset_time(&mut self) -> bool {
if self.state == 2 {
self.timer_csec = 0;
self.last_timer_csec = 0;
self.message.clear();
self.state = 0;
}
true
}
fn tick(&mut self) -> bool {
self.timer_csec += 1;
true
}
}
impl Component for MyTimer {
type Message = Msg;
type Properties = ();
fn create(ctx: &Context<Self>) -> Self {
MyTimer::new(ctx)
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::StartTime => self.start_time(ctx),
Msg::StopTime => self.stop_time(),
Msg::CountTime => self.conut_time(),
Msg::ResetTime => self.reset_time(),
Msg::UpdateTime => self.tick(),
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let mut button1 = html! {
<button onclick={ctx.link().callback(|_| Msg::CountTime)}>{"计次-"}</button>
};
let mut button2 = html! {
<button onclick={ctx.link().callback(|_| Msg::StartTime)}>{"开始"}</button>
};
if self.state == 1 {
button1 = html! {
<button onclick={ctx.link().callback(|_| Msg::CountTime)}>{"计次"}</button>
};
button2 = html! {
<button onclick={ctx.link().callback(|_| Msg::StopTime)}>{"停止"}</button>
};
} else if self.state == 2 {
button1 = html! {
<button onclick={ctx.link().callback(|_| Msg::ResetTime)}>{"重置"}</button>
};
button2 = html! {
<button onclick={ctx.link().callback(|_| Msg::StartTime)}>{"继续"}</button>
};
}
html! {
<>
<div id="time">
{ format!("{:0>2}:{:0>2}.{:0>2}",&self.timer_csec/6000,(&self.timer_csec%6000)/100,&self.timer_csec%100) }
</div>
<div id="buttons">
{ button1 }
{ button2 }
</div>
<div id="message">
{ for self.message.iter().map(|m| html!{ <li> {format!("{:#?}",m)} </li> })}
</div>
</>
}
}
}
</code></pre>
从CNF到DPLL算法
2022-03-12T00:00:00+00:00
2022-03-12T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-734/
<p>从三个问题出发:</p>
<ol>
<li>什么是CNF?</li>
<li>如何将逻辑表达式转化为CNF?</li>
<li>什么是DPLL算法?</li>
</ol>
<h2 id="he-qu-fan-shi-cnf-conjunctive-normal-form">合取范式(CNF,conjunctive normal form)</h2>
<p>CNF是指一系列逻辑表达式的合取,每一个子逻辑表达式都为下列类型之一:</p>
<ul>
<li>为原子表达式(例如:$ p $,$ \lnot p$)</li>
<li>为原子表达式的逻辑或(逻辑析取)(例如:$ p \lor q $,$ p \lor \lnot q $)</li>
<li>为原子表达式(例如:$ p $,$ \lnot p $)</li>
<li>为原子表达式的逻辑或(逻辑析取)(例如:$ p \lor q $,$ p \lor \lnot q $)</li>
</ul>
<h2 id="zhuan-hua-wei-cnf">转化为CNF</h2>
<p>将普通的逻辑表达式转化为CNF需要按一下四个步骤对现有表达式进行转换:</p>
<ol>
<li>用$ (\alpha \rightarrow \beta) \land (\beta \rightarrow \alpha) $替换$ \alpha \leftrightarrow \beta$</li>
<li>用$ \lnot \alpha \lor \beta$替换$ \alpha \rightarrow \beta $</li>
<li>替换
<ol>
<li>用$ \lnot \alpha \land \lnot \beta $替换$ \lnot (\alpha \lor \beta) $</li>
<li>用$ \lnot \alpha \lor \lnot \beta $替换$ \lnot (\alpha \land \beta) $</li>
<li>用$ \alpha$替换$ \lnot \lnot \alpha $ </li>
</ol>
</li>
<li>用$ (\alpha \lor \beta) \land (\alpha \lor \gamma) $替换$ \alpha \lor (\beta \land \gamma) $</li>
</ol>
<h3 id="li-zi">例子</h3>
<p>下面利用上面的步骤将一个逻辑表达式转化为CNF</p>
<p>$$
\begin{align*}
& \qquad(p \Leftrightarrow q)\Rightarrow \lnot r\ \cr
\rightarrow & \qquad ((p\Rightarrow q)\land (q\Rightarrow p))
\Rightarrow \lnot r &(1)\cr
\rightarrow &\qquad \lnot ((\lnot p \lor q)\land (\lnot q\lor p))\lor \lnot r &(2)\cr
\rightarrow &\qquad (\lnot(\lnot p \lor q)\lor \lnot(\lnot q\lor p))\lor \lnot r &(3.1)\cr
\rightarrow &\qquad ((\lnot \lnot p\land \lnot q)\lor(\lnot \lnot q\land \lnot p)) \lor \lnot r &(3.2)\cr
\rightarrow &\qquad ((p\land \lnot q)\lor(q\land \lnot p)) \lor \lnot r &(3.3)\cr
\rightarrow &\qquad (p\lor q\lor \lnot r)\land(p\lor \lnot p\lor \lnot r)
\land(\lnot q\lor q\lor \lnot r)\land(\lnot q\lor\lnot p\lor\lnot r) &(4)
\end{align*}
$$</p>
<h2 id="dpllsuan-fa">DPLL算法</h2>
<p><strong>DPLL</strong>(Davis-Putnam-Logemann-Loveland)<strong>算法</strong>,是一种完备的、以<a href="https://zh.wikipedia.org/wiki/%E5%9B%9E%E6%BA%AF">回溯</a>为基础的<a href="https://zh.wikipedia.org/wiki/%E7%AE%97%E6%B3%95">算法</a>,用于解决在<a href="https://zh.wikipedia.org/wiki/%E5%90%88%E5%8F%96%E8%8C%83%E5%BC%8F">合取范式</a>(CNF)中<a href="https://zh.wikipedia.org/wiki/%E5%91%BD%E9%A2%98%E9%80%BB%E8%BE%91">命题逻辑</a>的<a href="https://zh.wikipedia.org/wiki/%E5%B8%83%E7%88%BE%E5%8F%AF%E6%BB%BF%E8%B6%B3%E6%80%A7%E5%95%8F%E9%A1%8C">布尔可满足性问题</a>;也就是解决CNF-SAT问题。</p>
<h3 id="satwen-ti">SAT问题</h3>
<p>布尔可满足性问题(<strong>Boolean satisfiability
problem</strong>;**SAT **)属于<a href="https://zh.wikipedia.org/wiki/%E6%B1%BA%E5%AE%9A%E6%80%A7%E5%95%8F%E9%A1%8C">决定性问题</a>,也是第一个被证明属于<a href="https://zh.wikipedia.org/wiki/NP%E5%AE%8C%E5%85%A8">NP完全</a>的问题。此问题在<a href="https://zh.wikipedia.org/wiki/%E9%9B%BB%E8%85%A6%E7%A7%91%E5%AD%B8">计算机科学</a>上许多的领域皆相当重要,包括<a href="https://zh.wikipedia.org/w/index.php?title=%E9%9B%BB%E8%85%A6%E7%A7%91%E5%AD%B8%E5%9F%BA%E7%A4%8E%E7%90%86%E8%AB%96&action=edit&redlink=1">计算机科学基础理论</a>、<a href="https://zh.wikipedia.org/wiki/%E6%BC%94%E7%AE%97%E6%B3%95">算法</a>、<a href="https://zh.wikipedia.org/wiki/%E4%BA%BA%E5%B7%A5%E6%99%BA%E6%85%A7">人工智能</a>、<a href="https://zh.wikipedia.org/w/index.php?title=%E7%A1%AC%E9%AB%94%E8%A8%AD%E8%A8%88&action=edit&redlink=1">硬件设计</a>等等。</p>
<p>SAT问题,也叫作Boolean satisfiability
problem(布尔可满足性问题),属于<a href="https://zh.wikipedia.org/wiki/%E6%B1%BA%E5%AE%9A%E6%80%A7%E5%95%8F%E9%A1%8C">决定性问题</a>。具体指给定一组布尔表达式,指定其中每个布尔变量的取值,使得布尔表达式的值为TRUE。</p>
<h3 id="suan-fa-gai-shu">算法概述</h3>
<p>SAT问题是无法再多项式时间复杂度内解决的,DPLL算法也不例外。</p>
<p>DPLL算法是一种搜索算法,思想与DFS十分相似,也可以说DPLL是DFS在SAT问题上的一种实现,具体实现方法就是,算法总公式中会选择一个变量,将其赋值为True,化简赋值后的表达式,如果简化的公式是可能满足的(递归),那么原公式也是可满足的;否则就将变量复制为False,重新执行一遍递归判定,若不满足,那么原公式便是不可满足的。</p>
<pre data-lang="txt" class="language-txt "><code class="language-txt" data-lang="txt">dpll(CS,B) { % CS==set of clauses. B=Bindings of atoms to truth values
loop {
if (empty(CS)) return B;
if (emptyClause in CS) return Fail;
if (easyCaseIn(CS,B)) [CS,B] = easyCase(CS,B);
} % No easy cases
CSCopy = copy(CS); BCopy=Copy(B);
P = choose an unbound atom;
[CSCopy, BCopy] = propagate(CSCopy, BCopy, P, True);
answer = dpll(CSCopy,BCopy);
if (answer != Fail) return answer;
[CS, B] = propagate(CS, B, P, False);
return dpll(CS,B)
}
</code></pre>
Rust框架Yew支持CSS
2022-03-06T00:00:00+00:00
2022-03-06T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-707/
<p>Yew主框架并未开始支持CSS,但在<a href="https://github.com/yewstack/yew/issues/533">这里</a>统计了关于CSS支持的一些建议。</p>
<p>为了支持CSS,最直接的方法是在html!宏中直接添加class和id属性,然后在静态的index.html导入一个实现的CSS文件。其次可以使用一些其它的库来支持CSS。目前有5个库实现了Yew的CSS支持。</p>
<h2 id="yew">yew</h2>
<p><a href="https://github.com/dancespiele/yew_styles"><img src="https://gh-card.dev/repos/dancespiele/yew_styles.svg" alt="" /></a></p>
<p>styles是一个完全不依赖js的Yew样式库,但官网的教程中要用npm来进行安装,其支持的样式及布局可以参考<a href="https://yewstyles.spielrs.tech/layouts">这里</a>。此项目的目的是为Yew提供一个无需任何js依赖的样式框架,并实现一个类似于flexbox的布局系统。最近的更新在3个月前。</p>
<p><a href="https://github.com/dungeonfog/yew-mdc"><img src="https://gh-card.dev/repos/dungeonfog/yew-mdc.svg" alt="" /></a></p>
<p>yew-mdc是一个为Yew框架实现的<a href="https://material.io/design">Material Design</a>组件。最近更新在16个月前。</p>
<p><a href="https://github.com/AlephAlpha/muicss-yew"><img src="https://gh-card.dev/repos/AlephAlpha/muicss-yew.svg" alt="" /></a></p>
<p>MUICSS-yew
是基于 <a href="https://www.muicss.com/">MUI</a> 的CSS框架,<a href="https://alephalpha.github.io/muicss-yew/">这里</a>是它支持的样式布局。最近更新是11个月前。</p>
<p><a href="https://github.com/yewstack/yewtify"><img src="https://gh-card.dev/repos/yewstack/yewtify.svg" alt="" /></a></p>
<p><a href="https://vuetifyjs.com/en/">Vuetify</a>组件的Yew框架的实现。两年前更新的。</p>
<p><a href="https://github.com/futursolo/stylist-rs"><img src="https://gh-card.dev/repos/futursolo/stylist-rs.svg" alt="" /></a></p>
<p>stylist-rs通过css!和style!等宏直接将css写到rust中,两个月前更新。</p>
<p>另外<a href="https://github.com/jetli/awesome-yew#component-libraries">这里</a>总结了yew的一些项目和组件等。</p>
联想小新pro 13 windows11 bug记录(2022)
2022-03-05T00:00:00+00:00
2022-03-05T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-717/
<h2 id="ren-lian-shi-bie-wu-fa-zheng-chang-gong-zuo">人脸识别无法正常工作</h2>
<p>具体现象:windows Hello 无法录入人脸,一直卡在请确保你的面孔剧中。</p>
<p>解决方法:在设备管理器中卸载IR
Camera(勾选删除此设备的驱动程序)和Camera,重启之后就可以录入人脸了。</p>
<h2 id="window-xdao-zhi-zi-yuan-guan-li-qi-zhong-qi">Window+X导致资源管理器重启</h2>
<p><a href="https://www.ithome.com/0/604/913.htm">微软 Win11 预览版 22563 出现重大 Bug:Win + X 导致资源管理器崩溃,解决方案出炉 - IT之家 (ithome.com)</a>这里是关于此bug的具体详情及处理方案</p>
<p>更新后bug已解决</p>
Rust框架Yew的使用
2022-02-26T00:00:00+00:00
2022-02-26T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-709/
<h2 id="huan-jing-da-jian">环境搭建</h2>
<p>Yew框架也是将rust代码编译成WebAssembly,添加target</p>
<pre><code>rustup target add wasm32-unknown-unknown
</code></pre>
<p>安装<code>trunk</code>,他与<a href="https://rustwasm.github.io/wasm-pack/installer/">wasm-pack</a>类似,是将rust编译成wasm的重要工具。</p>
<pre><code>cargo install trunk
</code></pre>
<h2 id="shi-xian-hello-world">实现Hello World</h2>
<p>1.创建项目,用cargo创建一个新的project</p>
<pre><code>cargo new yew-app
</code></pre>
<p>2. 添加依赖,在Cargo.toml中添加yew依赖</p>
<pre data-lang="toml" class="language-toml "><code class="language-toml" data-lang="toml">[dependencies]
yew = { git = "https://github.com/yewstack/yew/" }
</code></pre>
<p>3.实现Hello World</p>
<pre data-lang="rust" class="language-rust "><code class="language-rust" data-lang="rust">use yew::prelude::*;
#[function_component(App)]
fn app() -> Html {
html! {
<h1>{ "Hello World" }</h1>
}
}
fn main() {
yew::start_app::<App>();
}
</code></pre>
<p>4.在项目根目录下创建index.html</p>
<pre data-lang="html" class="language-html "><code class="language-html" data-lang="html"><!DOCTYPE html>
<html
>
<head>
</head>
<body>
</body>
</html>
</code></pre>
<h2 id="qi-dong-webying-yong">启动Web应用</h2>
<pre><code>trunk serve
</code></pre>
将Rust程序编译成WebAssembly
2022-02-19T00:00:00+00:00
2022-02-19T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-697/
<h2 id="huan-jing-da-jian">环境搭建</h2>
<ol>
<li>从<a href="https://www.rust-lang.org/tools/install">这里</a>安装Rust工具链,rustup(Rust版本管理),rustc(Rust编译器),cargo(Rust包管理)</li>
<li>安装<a href="https://rustwasm.github.io/wasm-pack/installer/">wasm-pack</a>,用于构建、测试发布Rust生成的WebAssembly</li>
<li>安装cargo-generate,用于从模板快速启动一个Rsut项目,<code>cargo install cargo-generate</code></li>
<li>安装<a href="https://www.npmjs.com/">npm</a>,用于运行JavaScript
bundler和开发服务器</li>
</ol>
<h2 id="shi-xian-hello-world">实现Hello World</h2>
<p>1.克隆基础模板项目</p>
<pre><code>cargo generate --git https://github.com/rustwasm/wasm-pack-template
</code></pre>
<p>2.添加一个新的rust编译器</p>
<pre><code>rustup target add wasm32-unknown-unknown
</code></pre>
<p>3.编译</p>
<pre><code>wasm-pack build
</code></pre>
<h2 id="qi-dong-webfu-wu-qi">启动Web服务器</h2>
<p>1.初始化web项目</p>
<pre><code>npm init wasm-app www
</code></pre>
<p>2.进入目录www,初始化依赖</p>
<pre><code>npm install
</code></pre>
<p>3.在www项目中加载本地依赖wasm-app包</p>
<pre><code>{
// ...
"dependencies": { // Add this three lines block!
"wasm-app": "file:../pkg"
},
"devDependencies": {
//...
}
}
</code></pre>
<p>4.运行<code>npm install</code>在www项目中安装wasm-app依赖</p>
<p>5.修改文件<code>wasm-game-of-life/www/index.js</code>导入生成的库</p>
<p>6.启动服务器</p>
<pre><code>npm run start
</code></pre>
struct timespec转化为struct tm
2022-02-12T00:00:00+00:00
2022-02-12T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-698/
<p>C语言中遇到了三种时间类型:</p>
<ol>
<li>time_t</li>
<li>struct timespec</li>
<li>struct tm</li>
</ol>
<p>这里总结一下它们之间的相互转化</p>
<p>time_t 和 struct timespec</p>
<p>time_t 只存储秒而struct timespec存储了纳秒,直接将秒转化即可:</p>
<pre><code>time_t time = (time_t)ut_tv.tv_sec;
</code></pre>
<p>time_t 和 struct tm</p>
<ol>
<li><a href="https://www.cplusplus.com/reference/ctime/mktime/"><strong>mktime</strong></a>
将tm转化为time_t</li>
<li><a href="https://www.cplusplus.com/reference/ctime/localtime/"><strong>localtime</strong></a>
将time_t转化为tm(本地时区)</li>
<li><a href="https://www.cplusplus.com/reference/ctime/gmtime/"><strong>gmtime</strong></a>
将time_t转化为tm(UTC时区)</li>
</ol>
<p>timespec 和 tm 可以通过time_t 间接转换</p>
<h3 id="can-kao">参考</h3>
<ol>
<li><a href="http://ostack.cn/qa/?qa=1049564/"></a><a href="https://stackoverflow.com/questions/9252849/convert-timeval-to-time-t/9252913">c - Convert timeval to
time_t - Stack
Overflow</a></li>
<li><a href="https://www.cplusplus.com/reference/ctime/tm/">struct tm - C++ Reference
(cplusplus.com)</a></li>
</ol>
C语言红黑树实现
2022-02-05T00:00:00+00:00
2022-02-05T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-691/
<h2 id="hong-hei-shu-de-jie-gou">红黑树的结构</h2>
<p>类型定义包括两个部分:一个root节点和多个树节点</p>
<h3 id="rootjie-dian"><strong>root节点</strong></h3>
<p>root节点保存红黑树的树根节点</p>
<pre><code>typedef struct rb_tree() {
Node* node;
} RBRoot;
</code></pre>
<h3 id="shu-jie-dian"><strong>树节点</strong></h3>
<p>树节点用于保存红黑树节点中的信息包括</p>
<ul>
<li>节点的颜色~ ~color~ ~</li>
<li>节点存储的有效信息~ ~key~ ~</li>
<li>以及节点的左孩子节点~ ~left~ ~,右孩子节点~ ~right~ ~和父节点~ ~parent~ ~</li>
</ul>
<!-- -->
<pre><code>typedef struct RBTreeNode {
unsigned char color;
Type key;
struct RBTreeNode* left;
struct RBtreeNode* right;
struct RBtreeNode* parent;
} Node, *RBTree;
</code></pre>
<p>主要有六个函数用于实现红黑树:红黑树的<strong>创建</strong>与<strong>销毁</strong>,<strong>左旋</strong>,<strong>右旋</strong>,<strong>插入节点</strong>,<strong>删除节点</strong></p>
<h2 id="chuang-jian"><strong>创建</strong></h2>
<p>分配一个root节点</p>
<pre><code>RBRoot* create_rbtree() {
RBRoot* root = (RBRoot*) malloc( sizeof( RBRoot ) );
root->node = NULL;
return root;
}
</code></pre>
<h2 id="xiao-hui"><strong>销毁</strong></h2>
<p>销毁主要分四个步骤:</p>
<ol>
<li>销毁左子树</li>
<li>销毁右子树</li>
<li>释放树根节点空间</li>
<li>释放root节点空间</li>
</ol>
<!-- -->
<pre><code>static void rbtree_destroy( RBTree tree ) {
if ( tree == NULL ) return;
if ( tree->left != NULL ) rbtree_destroy( tree->left );
if ( tree->right != NULL ) rbtree_destroy( tree->right );
free( tree );
}
void destroy_rbtree( RBRoot* root ) {
if ( root != NULL ) rbtree_destroy( root->node );
free( root );
}
</code></pre>
<p>为了实现在红黑树的插入与删除后,并且保持红黑树的性质不变,需要用到下面两个对树的基本操作左旋和右旋</p>
<h2 id="zuo-xuan"><strong>左旋</strong></h2>
<div>
</div>
<p>左旋操作</p>
<p>对节点b进行左旋操作,进行左旋后减少了左子树的深度,增大了右子树的深度</p>
<pre><code>static void rbtree_left_rotate( RBRoot* root, Node* x ) {
Node* y = x->right;
x->right = y->left;
if ( y->left != NULL ) y->left->parent = x;
y->parent = x->parent;
if ( x->parent == NULL ) {
root->node = y;
} else {
if ( x->parent->left == x )
x->parent->left = y;
else
x->parent->right = y;
}
y->left = x;
x->parent = y;
}
</code></pre>
<h2 id="you-xuan"><strong>右旋</strong></h2>
<div>
</div>
<p>右旋操作</p>
<p>右旋操作与左旋操作相反,上图对节点a进行右旋操作,减少了右子树的深度,增大了左子树的深度</p>
<pre><code>static void rbtree_right_rotate( RBRoot* root, Node* y ) {
Node* x = y->left;
y->left = x->right;
if ( x->right != NULL ) x->right->parent = y;
x->parent = y->parent;
if ( y->parent == NULL ) {
root->node = x;
} else {
if ( y == y->parent->right )
y->parent->right = x;
else
y->parent->left = x;
}
x->right = y;
y->parent = x;
}
</code></pre>
<h2 id="cha-ru-jie-dian"><strong>插入节点</strong></h2>
<p>红黑树的插入分为两步:首先将待插入节点标记为红色并安装二叉查找树的方法插入;第二步对红黑树进行调整,使其保持红黑树的特性。根据不同的情况需要做出不同的调整:</p>
<ol>
<li>情况一:新节点N位于树的根上,没有父节点
<ol>
<li>直接将其标记为黑色即可</li>
</ol>
</li>
<li>情况二:新节点N的父节点P是黑色
<ol>
<li>直接插入,无需修正</li>
</ol>
</li>
<li>情况三:父节点P与叔父节点U都为红色
<ol>
<li>将父节点P与叔父节点U标记为黑色</li>
<li>将祖父节点G标记为红色</li>
<li>将祖父节点G作为新插入节点重新进行修正(重新执行修正)</li>
</ol>
</li>
<li>情况四:父节点P为红色,叔父节点U不为红色(新节点与父节点不同侧)
<ul>
<li>父节点P为祖父节点G的左子节点<strong>且</strong>新节点N为父节点P的右子节点
<ol>
<li>对节点N进行左旋操作</li>
<li>对节点N的左子树(左旋操作前的父节点P)执行情况5</li>
</ol>
</li>
<li>父节点P为祖父节点G的右子节点<strong>且</strong>新节点N为父节点P的左子节点
a. 对节点N进行右旋操作
b. 对节点N的右子树(右旋操作前的父节点P)执行情况5</li>
</ul>
</li>
<li>情况五:父节点P为红色,叔父节点U不为红色
<ol>
<li>将父节点P标记为黑色</li>
<li>将祖父节点G标记为红色</li>
<li>父节点P为祖父节点G的右子节点<strong>且</strong>新节点N为父节点P的右子节点
<ol>
<li>对父节点P进行右旋操作</li>
</ol>
</li>
<li>父节点P为祖父节点G的左子节点<strong>且</strong>新节点N为父节点P的左子节点
<ol>
<li>对父节点P进行左旋操作</li>
</ol>
</li>
</ol>
</li>
</ol>
<!-- -->
<pre><code>static void rbtree_insert_fixup( RBRoot* root, Node* node ) {
Node *parent, *gparent;
while ( ( parent = rb_parent( node ) ) && rb_is_red( parent ) ) {
gparent = rb_parent( parent );
if ( parent == gparent->left ) {
{
Node* uncle = gparent->right;
if ( uncle && rb_is_red( uncle ) ) {
rb_set_black( uncle );
rb_set_black( parent );
rb_set_red( gparent );
node = gparent;
continue;
}
}
if ( parent->right == node ) {
Node* tmp;
rbtree_left_rotate( root, parent );
tmp = parent;
parent = node;
node = tmp;
}
rb_set_black( parent );
rb_set_red( gparent );
rbtree_right_rotate( root, gparent );
} else {
{
Node* uncle = gparent->left;
if ( uncle && rb_is_red( uncle ) ) {
rb_set_black( uncle );
rb_set_black( parent );
rb_set_red( gparent );
node = gparent;
continue;
}
}
if ( parent->left == node ) {
Node* tmp;
rbtree_right_rotate( root, parent );
tmp = parent;
parent = node;
node = tmp;
}
rb_set_black( parent );
rb_set_red( gparent );
rbtree_left_rotate( root, gparent );
}
}
rb_set_black( root->node );
}
static void rbtree_insert( RBRoot* root, Node* node ) {
Node* y = NULL;
Node* x = root->node;
while ( x != NULL ) {
y = x;
if ( node->key < x->key )
x = x->left;
else
x = x->right;
}
rb_parent( node ) = y;
if ( y != NULL ) {
if ( node->key < y->key )
y->left = node;
else
y->right = node;
} else {
root->node = node;
}
node->color = RED;
rbtree_insert_fixup( root, node );
}
int insert_rbtree( RBRoot* root, Type key ) {
Node* node;
if ( search( root->node, key ) != NULL ) return -1;
if ( ( node = create_rbtree_node( key, NULL, NULL, NULL ) ) == NULL )
return -1;
rbtree_insert( root, node );
return 0;
}
</code></pre>
<h2 id="shan-chu-jie-dian"><strong>删除节点</strong></h2>
<p>节点的删除也分为两个步骤:<strong>节点的删除</strong>与<strong>删除后的修正</strong></p>
<p><strong>节点的删除</strong>分为三种情况:</p>
<ol>
<li>节点没有子树,直接删除</li>
<li>节点只有一个子树,用子节点替代此节点,在子节点上进行修正</li>
<li>节点有两个子树,用右子树最小值节点替代此节点,在新节点上进行修正</li>
</ol>
<p>对于<strong>删除后的修正</strong>,如果删除节点是红色,则无需修正,否则需要修正,分多种情况:</p>
<ol>
<li>情况一:N是新的根
<ol>
<li>无需修正</li>
</ol>
</li>
<li>情况二:兄弟节点S是红色
<ol>
<li>对父节点P进行左旋,交换N的父节点P与祖父节点G的颜色</li>
<li>进入情况三</li>
</ol>
</li>
<li>情况三:N的父节点G,兄弟节点S和S的儿子都是黑色
<ol>
<li>将兄弟节点S设置为红色</li>
<li>对父节点P从<strong>情况一</strong>开始重新修正</li>
</ol>
</li>
<li>情况四:N的父节点G为红色,兄弟节点S和S的儿子都是黑色
<ol>
<li>交换N的兄弟和父亲的颜色</li>
</ol>
</li>
<li>情况五:N的父节点G为红色,兄弟节点S为红色,S的右儿子为黑色,N为其父节点的左儿子
<ol>
<li>在S上做右旋</li>
<li>交换S和它的父节点颜色</li>
<li>进入情况六</li>
</ol>
</li>
<li>情况六:S是黑色,S的右儿子是红色,N为其父节点的左儿子
<ol>
<li>在N的父节点P上做左旋</li>
<li>交换N和父节点的颜色</li>
<li>将S的右子树设置为黑色</li>
</ol>
</li>
</ol>
<!-- -->
<pre><code>static void rbtree_delete_fixup( RBRoot* root, Node* node, Node* parent ) {
Node* other;
while ( ( !node || rb_is_black( node ) ) && node != root->node ) {
if ( parent->left == node ) {
other = parent->right;
if ( rb_is_red( other ) ) {
rb_set_black( other );
rb_set_red( parent );
rbtree_left_rotate( root, parent );
other = parent->right;
}
if ( ( !other->left || rb_is_black( other->left ) ) &&
( !other->right || rb_is_black( other->right ) ) ) {
rb_set_red( other );
node = parent;
parent = rb_parent( node );
} else {
if ( !other->right || rb_is_black( other->right ) ) {
rb_set_black( other->left );
rb_set_red( other );
rbtree_right_rotate( root, other );
other = parent->right;
}
rb_set_color( other, rb_color( parent ) );
rb_set_black( parent );
rb_set_black( other->right );
rbtree_left_rotate( root, parent );
node = root->node;
break;
}
} else {
other = parent->left;
if ( rb_is_red( other ) ) {
rb_set_black( other );
rb_set_red( parent );
rbtree_right_rotate( root, parent );
other = parent->left;
}
if ( ( !other->left || rb_is_black( other->left ) ) &&
( !other->right || rb_is_black( other->right ) ) ) {
rb_set_red( other );
node = parent;
parent = rb_parent( node );
} else {
if ( !other->left || rb_is_black( other->left ) ) {
rb_set_black( other->right );
rb_set_red( other );
rbtree_left_rotate( root, other );
other = parent->left;
}
rb_set_color( other, rb_color( parent ) );
rb_set_black( parent );
rb_set_black( other->left );
rbtree_right_rotate( root, parent );
node = root->node;
break;
}
}
}
if ( node ) rb_set_black( node );
}
void rbtree_delete( RBRoot* root, Node* node ) {
Node *child, *parent;
int color;
if ( ( node->left != NULL ) && ( node->right != NULL ) ) {
Node* replace = node;
replace = replace->right;
while ( replace->left != NULL ) {
replace = replace->left;
}
if ( rb_parent( node ) ) {
if ( rb_parent( node )->left == node ) {
rb_parent( node )->left = replace;
} else {
rb_parent( node )->right = replace;
}
} else {
root->node = replace;
}
child = replace->right;
parent = rb_parent( replace );
color = rb_color( replace );
if ( parent == node ) {
parent = replace;
} else {
if ( child ) rb_set_parent( child, parent );
parent->left = child;
replace->right = node->right;
rb_set_parent( node->right, replace );
}
replace->parent = node->parent;
replace->color = node->color;
replace->left = node->left;
node->left->parent = replace;
if ( color == BLACK ) rbtree_delete_fixup( root, child, parent );
free( node );
return;
}
if ( node->left != NULL ) {
child = node->left;
} else {
child = node->right;
}
parent = node->parent;
color = node->color;
if ( child ) child->parent = parent;
if ( parent ) {
if ( parent->left == node ) {
parent->left = child;
} else {
parent->right = child;
}
} else {
root->node = child;
}
if ( color == BLACK ) rbtree_delete_fixup( root, child, parent );
free( node );
}
void delete_rbtree( RBRoot* root, Type key ) {
Node *z, *node;
if ( ( z = search( root->node, key ) ) != NULL ) rbtree_delete( root, z );
}
</code></pre>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://github.com/torvalds/linux/blob/5bfc75d92efd494db37f5c4c173d3639d4772966/tools/include/linux/rbtree.h">linux/rbtree.h at 5bfc75d92efd494db37f5c4c173d3639d4772966 ·
torvalds/linux
(github.com)</a></li>
<li><a href="https://github.com/torvalds/linux/blob/5bfc75d92efd494db37f5c4c173d3639d4772966/lib/rbtree.c">linux/rbtree.c at 5bfc75d92efd494db37f5c4c173d3639d4772966 ·
torvalds/linux
(github.com)</a></li>
</ol>
nginx ssl配置问题(curl 60错误)
2022-01-10T00:00:00+00:00
2022-01-10T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-677/
<p>通过<a href="https://github.com/acmesh-official/acme.sh">acme.sh</a>工具给网站配置了ssl,按照流程配置成功后,使用浏览器可以正常访问,但使用curl进行访问时,出现如下错误:</p>
<pre><code>curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
</code></pre>
<p>由于浏览器能够访问成功,就主观认为是系统curl和系统证书的问题。</p>
<p>找到了<a href="https://stackoverflow.com/questions/24611640/curl-60-ssl-certificate-problem-unable-to-get-local-issuer-certificate">这里</a>,尝试了各种方法都不行。</p>
<p>最终找到这个<a href="https://whatsmychaincert.com/">网站</a>对自己网站的ssl进行测试,结果是:</p>
<pre><code>****.com is misconfigured. This is the chain it should be using.
</code></pre>
<p>于是怀疑是自己nginx配置有问题,查看网站给出的证书文件(chain),比自己配置的多了一部分。</p>
<p>网站上的有两个<code>CERTIFICATE</code>段,而acme.sh生成的只有一个。直接从<code>whatsmychaincert</code>网站下载fallchain证书。</p>
<p>在服务器上重新配置,并重启nginx,curl就可以访问了。</p>
<p>对比nginx和apache的配置:</p>
<pre><code>acme.sh --install-cert -d example.com \
--cert-file /path/to/certfile/in/apache/cert.pem \
--key-file /path/to/keyfile/in/apache/key.pem \
--fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \
--reloadcmd "service apache2 force-reload"
acme.sh --install-cert -d example.com \
--key-file /path/to/keyfile/in/nginx/key.pem \
--fullchain-file /path/to/fullchain/nginx/cert.pem \
--reloadcmd "service nginx force-reload"
</code></pre>
<p>可以看到apache多配置了一个 --cert-file 参数,而nginx只配置了
--fullchain-file
,仔细看了一下nginx的证书安装命令,原来是安装了cert文件,忘记安装fullchain文件了。</p>
<p>大意了。。。</p>
<p>明明参数是<code>--fullchain-file</code>为啥文件名要是<code>cert.pem</code></p>
WSL2联网问题
2022-01-08T00:00:00+00:00
2022-01-08T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-675/
<p>使用wsl做开发有一段时间了,最近突然遇到在wsl中无法联网,查找原因发现是因为无法解析域名。</p>
<p>查看文件<code>/etc/resolv.conf</code>,里面解释文件是wsl启动时生成的,将文件里的<code>namespace</code>更改为8.8.8.8后,可以正常访问网络,但是重启之后,文件<code>/etc/resolv.conf</code>恢复成原来的了。</p>
<p>经过一番查找,按如下方法修改后,文件/etc/resolv.conf不在会改变。</p>
<pre><code>sudo rm /etc/resolv.conf
sudo bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf'
sudo bash -c 'echo "[network]" > /etc/wsl.conf'
sudo bash -c 'echo "generateResolvConf = false" >> /etc/wsl.conf'
sudo chattr +i /etc/resolv.conf
</code></pre>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://github.com/microsoft/WSL/issues/5420">WSL 2 keeps overwriting resolv.conf · Issue #5420 · microsoft/WSL
(github.com)</a></li>
<li><a href="https://github.com/microsoft/WSL/issues/1908#issuecomment-641396913">Custom resolv.conf being replaced by generated file, even with
comment removed. · Issue #1908 · microsoft/WSL
(github.com)</a></li>
<li><a href="https://docs.microsoft.com/zh-cn/windows/wsl/wsl-config">WSL 中的高级设置配置 | Microsoft
Docs</a></li>
</ol>
docker 交叉编译
2022-01-02T00:00:00+00:00
2022-01-02T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-667/
<ol>
<li>查看存在的builder:~ ~<code>docker buildx ls</code></li>
<li>创建一个新的builder:<code>docker buildx create --name mybuilder</code></li>
<li>使用新的builder:<code>docker buildx use mybuilder && docker buildx inspect --bootstrap</code><br />
<code>docker buildx inspect --bootstrap</code>命令出错:<br />
<code>Error:error getting credentials - err: exit status 1, out: `Failed to execute child process “dbus-launch” (No such file or directory)</code><br />
根据<a href="https://github.com/moby/moby/issues/34175">这里</a>提示,安装了两个软件包:<br />
<code>apt install dbus-x11</code><br />
<code>apt install gnupg2 pass</code></li>
<li>构建镜像:<code>docker buildx build --platform linux/arm64/v7 -t name:proxy_pool .</code><br />
平台可以选择<code>linux/amd64</code>,<code>linux/arm64</code>,<code>linux/arm/v7</code>等</li>
</ol>
<h2 id="can-kao"><strong>参考</strong></h2>
<ol>
<li><a href="https://docs.docker.com/desktop/multi-arch/">Leverage multi-CPU architecture support | Docker
Documentation</a></li>
</ol>
docker镜像alpine无法联网
2022-01-01T00:00:00+00:00
2022-01-01T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2022/wp-665/
<p>alpine docker镜像无法联网出现如下错误:</p>
<pre><code>ERROR: https://mirrors.ustc.edu.cn/alpine/v3.15/main: temporary error (try again later) WARNING: Ignoring https://mirrors.ustc.edu.cn/alpine/v3.15/main: No such file or directory 3069277072:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed:ssl/statem/statem_clnt.c:1914:
</code></pre>
<p>基础的 docker 镜像是~ ~python:3.6-alpine~ ~</p>
<p><strong>stack overflow上看到一个解答</strong></p>
<p><a href="https://stackoverflow.com/questions/53710206/docker-cant-build-because-of-alpine-error">docker can't build because of alpine error - Stack
Overflow</a></p>
<ol>
<li>使用~ ~<code>docker build . --network local </code>进行构建--错误仍在</li>
<li>添加~ ~8.8.8.8~ ~到文件<code> /etc/resolv.conf </code>并重启docker--错误仍在</li>
</ol>
<p><strong>思路:启动一个基础docker 容器,并进入查找错误原因</strong></p>
<ol>
<li>启动并进入容器<br />
<code>docker run -it python:3.6-alpine /bin/sh </code></li>
<li>执行Dockerfile文件里的命令<br />
apk安装及更新时出错<br />
使用~ ~ping~ ~命令测试网络连通性,出错~ ~ping:
clock_gettime(MONOTONIC) failed~ ~<br />
使用~ ~nc~ ~命令连接外部服务器,可以连接ip,不能连接域名<br />
猜想应该是域名解析的问题</li>
</ol>
<p><strong>对alpine不熟,换一个其他的基础容器</strong></p>
<p>使用镜像~ ~python:3.6~ ~,启动镜像后使用pip时报错</p>
<pre><code>Fatal Python error: Py_Initialize: can't initialize time
PermissionError: [Errno 1] Operation not permitted
Current thread 0xb6fda010 (most recent call first):
Aborted (core dumped)
# pip
Fatal Python error: Py_Initialize: can't initialize time
PermissionError: [Errno 1] Operation not permitted
Current thread 0xb6f12010 (most recent call first):
Aborted (core dumped)
</code></pre>
<p>比较了一下两个python镜像的大小</p>
<pre><code>python 3.6 a3bba401d788 2 weeks ago 696MB
python 3.6-alpine dc8c4f7b64b7 2 weeks ago 34.8MB
</code></pre>
<p>还是用~ ~alpine~ ~版本的吧</p>
<p><strong>域名解析问题</strong></p>
<p>搜到<a href="https://www.sudops.com/kubernetes-alpine-image-resolve-ext-dns.html">这里</a>,说要将~ ~/etc/resolv.conf~ ~文件中的~ ~ndots~ ~注释掉</p>
<p>进入容器查看~ ~/etc/resolv.conf~ ~文件,并没有字符~ ~ndots~ ~</p>
<p>搜索ping的错误~ ~ping: clock_gettime(MONOTONIC) failed~ ~</p>
<p>找到了<a href="https://docs.linuxserver.io/faq#libseccomp">这里</a>,有三种解决方法</p>
<ol>
<li>安装一个软件包<br />
<code>wget http://ftp.us.debian.org/debian/pool/main/libs/libseccomp/libseccomp2_2.4.4-1~bpo10+1_armhf.deb && sudo dpkg -i libseccomp2_2.4.4-1~bpo10+1_armhf.deb</code><br />
下载时发现2.4.4版本已经没了,于是下载2.5.1版本<br />
安装后仍然时间还不对,安装前是1970年,安装后变成了2071年</li>
<li>第二个方法一样,不过是添加源安装软件</li>
<li>方法三要更新树莓派的系统</li>
</ol>
<p>方法2和方法3需要添加源或更新系统,不想搞太多源,也懒得重新安装</p>
<p>又搜了一下,看到<a href="https://gitlab.alpinelinux.org/alpine/aports/-/issues/12091">这里</a>,有人提示添加~ ~--security-opt
seccomp=unconfined~ ~</p>
<p>测试一下</p>
<pre><code>❯ docker run -it --rm --security-opt seccomp=unconfined python:3.6-alpine ping baidu.com
PING baidu.com (220.181.38.148): 56 data bytes
64 bytes from 220.181.38.148: seq=0 ttl=48 time=45.827 ms
64 bytes from 220.181.38.148: seq=1 ttl=48 time=47.090 ms
</code></pre>
<p>网络可以ping通了,应该可行</p>
<p>但是使用~ ~docker build~ ~时,出现了问题</p>
<pre><code>❯ docker build . --security-opt seccomp=unconfined
Error response from daemon: The daemon on this platform does not support setting security options on build
</code></pre>
<p>系统无法使用此选项</p>
<p><strong>从其它系统构建镜像后导出</strong></p>
<p>docker 镜像保存:</p>
<p>~ ~docker save 0d33 > proxy_pool.tar~ ~</p>
<p>docker镜像导入:</p>
<p>~ ~docker load < proxy_pool.tar~ ~</p>
<p>两个机器架构不同</p>
<p>按照<a href="https://blog.vhcffh.com/667.html">这里</a>进行跨平台构建即可</p>
cargo报错
2021-12-19T00:00:00+00:00
2021-12-19T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-661/
<p>遇到一个cargo错误</p>
<pre><code>error: no override and no default toolchain set
</code></pre>
<p>用rustup重新下载并设置默认版本</p>
<pre><code>rustup install stable
rustup default stable
</code></pre>
<p>下载更新完成后就无错误了</p>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="http://hongouru.blogspot.com/2018/11/solved-arch-linux-error-no-default.html?m=0">HELP GRANTED : [ SOLVED ] Arch Linux error: no default toolchain
configured
(hongouru.blogspot.com)</a></li>
</ol>
WIFI通信
2021-12-18T00:00:00+00:00
2021-12-18T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-600/
<p>WIFi是以<a href="https://zh.wikipedia.org/wiki/IEEE_802.11">IEEE
802.11</a>(一种无线局域网标准)为标准的一种实现。随着标准的不断修订,其传输速度在不断增加。现今第六代的传输速率可达9.6Gbit/s。下表为第一代到第六代的相关信息。对每一代仅列出了比较常见的标准,另外也有一些不常见的<a href="https://zh.wikipedia.org/wiki/IEEE_802.11_(%E5%8E%9F%E5%A7%8B%E6%A0%87%E5%87%86)">标准</a>。在多个厂商兼容802.11b后,802.11网络才开始大规模流行。</p>
<h2 id="wang-luo-cheng-yuan-he-jie-gou">网络成员和结构</h2>
<ul>
<li>站点(Station),网络最基本的组成部分。</li>
<li>基本服务单元(Basic Service
Set,BSS)。网络最基本的服务单元。最简单的服务单元可以只由两个站点组成。站点可动态连结(associate)到基本服务单元中。</li>
<li>分配系统(Distribution
System,DS)。分配系统用于连结不同的基本服务单元。逻辑上,分配系统使用的介质(Medium)和基本服务单元使用的介质完全不同,尽管物理上它们可能会是同一个介质,例如同一个无线频段。</li>
<li>接入点(Access
Point,AP)。接入点即有普通站点的身份,又有连接到分配系统的功能。</li>
<li>扩展服务单元(Extended Service
Set,ESS)。由分配系统和基本服务单元组合而成。这种组合是逻辑上,并非物理上。不同的基本服务单元物有可能在地理位置相去甚远。分配系统也可以使用各种各样的技术。</li>
<li>关口(Portal)。也是一个逻辑成分,用于将无线局域网和有线<a href="https://zh.wikipedia.org/wiki/%E5%B1%80%E5%9F%9F%E7%BD%91">局域网</a>或其它网络联系起来。</li>
</ul>
<p>这里有3种介质,站点使用的无线介质,分配系统使用的介质,以及和无线局域网集成一起的其它<a href="https://zh.wikipedia.org/wiki/%E5%B1%80%E5%9F%9F%E7%BD%91">局域网</a>使用的介质。物理上它们可能互相重叠。<a href="https://zh.wikipedia.org/wiki/IEEE_802.11">IEEE
802.11</a>只负责在站点使用的无线的介质上的寻址(Addressing)。分配系统和其它局域网的寻址不属无线局域网的范围。</p>
<p>IEEE802.11没有具体定义分配系统,只是定义了分配系统应该提供的服务(Service)。整个无线局域网定义了9种服务:</p>
<ul>
<li>5种服务属于分配系统的任务,分别为,连接(Association)、结束连接(Diassociation)、分配(Distribution)、集成(Integration)、再连接(Reassociation)。</li>
<li>4种服务属于站点的任务,分别为,鉴权(Authentication)、结束鉴权(Deauthentication)、隐私(Privacy)、MAC数据传输(MSDU
delivery)。</li>
</ul>
<h2 id="zheng-jie-gou">帧结构</h2>
<p>一般802.11的帧结构由9个字段构成。</p>
<p>802.11帧结构</p>
<h3 id="Frame-Control">Frame Control</h3>
<p>Frame control</p>
<p>所有的802.11帧都是由2字节的帧控制字段开始的,包括如下的字段:</p>
<ul>
<li>Protocol([0:1]):表示该帧所使用的MAC版本,目前只有一个版本协议编号为0。</li>
<li>Type([2:3]):表示帧类型。在802.11中定义了三种帧分别是:管理帧(Type=00b)、控制帧(Type=01b)、数据帧(Type=10b)。</li>
<li>Subtype([4:7]):对于Type定义的三种帧,进行了进一步的细化,详细参见表1。</li>
<li>ToDs([8]):表示该帧是BSS向DS发送的帧。</li>
<li>FromDs([9]):表示该帧是DS向BSS发送的帧。</li>
<li>More
Frag([10]):说明了长帧分包发送的情况,为1表示该帧之后还有其它帧,为0表示该帧为最后一帧。</li>
<li>Retry([11]):任何重传的帧都会将此位设定为1,以协助接收端剔除重复的帧。</li>
<li>Pwr
Mgmt([12]):只是该帧传送结束之后,Station应采用的电源管理模式。1表示Station即将进入省电(power-save)模式,
0表示将进入激活(active)模式。</li>
<li>More
Data([13]):针对处于省电模式的Station,AP会将从分布系统中接收来的帧加以缓存。AP将此位置1表示至少还有一帧需要传给休眠中的Station。</li>
<li>Protected Frame([14]):如果该帧经过WEP(Wired Equivalent
Privacy)加密处理则置1,否则置0。</li>
<li>Order([15]):在分段传送长帧的时候,将该位置1,表示接收端需要严格按照顺序处理该帧。</li>
</ul>
<h3 id="duration-id">Duration ID</h3>
<p>Duration
ID(持续时间)占2个字节,主要用来记载网络分配矢量NAC的值,用来限制访问介质的时间。</p>
<h3 id="address">Address</h3>
<p>address有五种类型:</p>
<ol>
<li>BSSID,基本服务集标识符</li>
<li>DA,目的地址</li>
<li>SA,源地址</li>
<li>RA,接收端地址</li>
<li>TA,发送端地址</li>
</ol>
<p>根据Frame Control中的ToDS和FromDS来确定其具体功能:</p>
<table><thead><tr><th>功能</th><th>To DS</th><th>From DS</th><th>Address1(接收端)</th><th>Address2(发送端)</th><th>Address3</th><th>Address4</th></tr></thead><tbody>
<tr><td>IBSS</td><td>0</td><td>0</td><td>DA</td><td>SA</td><td>BSSID</td><td>未使用</td></tr>
<tr><td>To AP(基础结构型)</td><td>1</td><td>0</td><td>BSSID</td><td>SA</td><td>DA</td><td>未使用</td></tr>
<tr><td>From AP(基础结构型)</td><td>0</td><td>1</td><td>DA</td><td>BSSID</td><td>SA</td><td>未使用</td></tr>
<tr><td>WDS(无线分布式系统)</td><td>1</td><td>1</td><td>RA</td><td>TA</td><td>DA</td><td>SA</td></tr>
</tbody></table>
<h3 id="seq-crl">Seq-crl</h3>
<p>Seq-crl(Sequence
Control)格式式如下,顺序编号4096的模数,从0开始,每处理一个上层封包就加1<br />
若上层封包分片处理,所有帧分片采用相同顺序编号;对于重传帧,顺序编号不变</p>
<h3 id="frame-bodyzi-duan">Frame Body字段</h3>
<p>Frame
Body字段中记录的是通信中实际的数据,所以该字段也是数据字段。它最多可以传送2304个字节的数据(为了能够支持<a href="https://zh.wikipedia.org/wiki/%E6%9C%89%E7%B7%9A%E7%AD%89%E6%95%88%E5%8A%A0%E5%AF%86">WEP</a>(无线加密协议),
各供应商在具体实现的时候通常是支持2312个字节的数据)。802.11的<a href="https://zh.wikipedia.org/wiki/%E9%80%BB%E8%BE%91%E9%93%BE%E8%B7%AF%E6%8E%A7%E5%88%B6">LLC</a>(逻辑链路控制)需要占8个字节,因此最多可以承载2296个字节数据。</p>
<h3 id="fcszi-duan">FCS字段</h3>
<p>FCS字段叫做帧校验序列(Frame Check Sequence,
FCS),主要用来进行校验数据,确保传输过程没有发生错误(如果发生错误,将进行重传)。通常使用循环冗余校验<a href="https://zh.wikipedia.org/wiki/%E5%BE%AA%E7%92%B0%E5%86%97%E9%A4%98%E6%A0%A1%E9%A9%97">CSC</a>(Cyclic
Redundancy Check)校验位进行校验。</p>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://zh.wikipedia.org/wiki/Wi-Fi">Wi-Fi - 维基百科,自由的百科全书 (wikipedia.org)</a></li>
<li><a href="https://zh.wikipedia.org/wiki/IEEE_802.11">IEEE 802.11 - 维基百科,自由的百科全书 (wikipedia.org)</a></li>
<li><a href="https://gaoyichao.com/Xiaotu/?book=network_and_security&title=802.11%E5%B8%A7%E7%BB%93%E6%9E%84">无处不在的小土-802.11帧结构 (gaoyichao.com)</a></li>
<li><a href="https://cxymm.net/article/fengfeng0328/112798288">无线射频专题《协议类,IEEE 802.11/802.11b/802.11a/802.11g/802.11n/802.11ac标准简介》_物联网研究室-BBC的博客-程序员秘密 - 程序员秘密 (cxymm.net)</a></li>
<li><a href="http://codetd.com/article/12026114">802.11 PHY层帧格式汇总 - 代码天地 (codetd.com)</a></li>
<li><a href="http://codetd.com/article/8450730">802.11帧格式 - 代码天地 (codetd.com)</a></li>
<li><a href="https://www.cnblogs.com/rougungun/p/14340489.html">802.11 帧格式和分类详解 - 肉滚滚和代码 - 博客园 (cnblogs.com)</a></li>
<li><a href="https://www.cnblogs.com/hzl6255/p/4084173.html">802.11 MAC层 - 北落不吉 - 博客园 (cnblogs.com)</a></li>
</ol>
tar命令详解
2021-12-04T00:00:00+00:00
2021-12-04T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-631/
<p>解包命令:<code>tar -xvf all.tar</code><br />
打包命令:<code>tar -cvf all.tar dir1 dir2 1.txt 2.txt</code><br />
解压命令:<code>tar -x[zjZ][v]f all.tar.[gz/bz2/Z]</code><br />
压缩命令:<code>tar -c[zjZ]f all.tar.[gz/bz2/Z]</code></p>
<p>更新压缩档案中的文件</p>
<p>压缩解压参数</p>
<p>这五个参数是完全独立的,必须且只能使用其中一个与另外的参数配合进行某项操作。</p>
<p>压缩为compress(后缀Z)</p>
<p>压缩解压方案参数</p>
<p>这三个参数用来选择一个压缩方案,如果没有选择任何方案,则仅进行打包解包命令(后缀tar)。</p>
<p>输出文件到标准输出</p>
<p>其它配置</p>
<p>这两个参数添加一些其它特性。它们也是可选择。</p>
<h2 id="zong-jie">总结</h2>
<p><code>unzip file.zip</code></p>
<p>压缩解压示例</p>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://www.cnblogs.com/xinghen1216/p/11307956.html">(21)tar打包命令详解 - 星痕1216 - 博客园
(cnblogs.com)</a></li>
<li><a href="https://www.cnblogs.com/xiondun/p/13208881.html">tar 压缩命令 - xiondun - 博客园
(cnblogs.com)</a></li>
</ol>
C语言日期时间
2021-11-28T00:00:00+00:00
2021-11-28T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-625/
<p>在本教程中,我们将展示如何在 C 中使用日期和时间。在本教程中,我们使用
C99。</p>
<h2 id="c-ri-qi-shi-jian-ding-yi">C 日期时间定义</h2>
<p>我们从几个定义开始。<strong>日历时间</strong>,也称为绝对时间,是时间连续体中的一个点,例如
2016 年 2 月 17 日 13:02:5
CET。<strong>时间间隔</strong>是两个日历时间之间时间连续体的连续部分,例如 2000 年 2
月 20 日 13:00 到 14:00 之间的小时。<em>经过的时间</em>是间隔的长度,例如 28
分钟。</p>
<p><strong>时间量</strong>是经过的时间之和。经过的时间不需要是连续的。当工作花了我们十一个小时,我们可能是在不同的日子工作。<strong>CPU
时间</strong>是中央处理单元 (CPU)
用于处理计算机程序或操作系统指令的时间量。它以系统时钟或秒为单位进行测量。<strong>Unix
纪元</strong>是指从1970 年 1 月 1 日 00:00:00 UTC 开始的时间(或
1970-01-01T00:00:00Z ISO 8601)。</p>
<p><strong>Unix 时间</strong>是自<strong>Unix 纪元</strong>以来经过的时间的秒数。 <strong>Unix
时间</strong>的类型为<code>time_t</code>。另外有一个<strong>时间结构体</strong>来表示人类可读的日历时间。它由一组包括年、月、日、时、分、秒等变量组成。<strong>时间结构体</strong>的类型为<code>struct tm</code>。</p>
<p><strong>现实时间</strong>(Wall time、real-world time、wall-clock
time)不同于通过计算机微处理器时钟脉冲或周期测量得到的时间。</p>
<p><code>time.h</code>包含了以下函数:</p>
<ul>
<li><code>char *asctime(const struct tm *)</code> --- 将时间转化为字符串(过时的)</li>
<li><code>clock_t clock(void)</code> --- 返回当前进程启动后使用的CPU时间</li>
<li><code>int clock_getres(clockid_t, struct timespec *)</code> --- 返回时钟精度</li>
<li><code>int clock_gettime(clockid_t, struct timespec *)</code> ---
用于计算时间,有秒和纳秒两种精度;比clock精确</li>
<li><code>int clock_settime(clockid_t, const struct timespec *)</code> ---
设置系统时间</li>
<li><code>char *ctime(const time_t *)</code> ---
将时间值转换为日期和时间字符串(过时)</li>
<li><code>double difftime(time_t, time_t)</code> --- 计算两个时间的差</li>
<li><code>struct tm *getdate(const char *)</code> --- 将时间字符串转换为时间结构体</li>
<li><code>struct tm *gmtime(const time_t *)</code> ---
将Unix时间转换为时间结构体(<a href="https://baike.baidu.com/item/%E5%8D%8F%E8%B0%83%E4%B8%96%E7%95%8C%E6%97%B6/787659">UTC时间</a>)</li>
<li><code>struct tm *localtime(const time_t *)</code> ---
将Unix时间转换为时间结构体(本地时间)</li>
<li><code>time_t mktime(struct tm *)</code> --- 将时间结构体转换为Unix时间</li>
<li><code>size_t strftime(char *, size_t, const char *, const struct tm *)</code> ---
以指定格式输出时间字符串</li>
<li><code>char *strptime(const char *, const char *, struct tm *)</code> ---
以指定格式的时间字符串配置时间结构体</li>
<li><code>time_t time(time_t *)</code> --- 返回Unix时间或设置Unix时间</li>
<li><code>void tzset(void)</code> --- 设置时区信息</li>
</ul>
<p>此外,<code>time.h</code>文件定义了<code>CLOCKS_PER_SEC</code>宏,它保存了每秒的处理器时钟刻度数。
<code>clock_t</code>是进程运行时间类型。</p>
<h2 id="unixshi-jian-time-t">Unix时间(time_t)</h2>
<p>Unix时间是指自Unix纪元以来的秒数。
time()函数返回自1970年1月1日协调世界时0小时0分0秒以来的时间值,单位为秒。
如果发生错误,它返回-1。</p>
<pre><code>time_t time(time_t *t);
</code></pre>
<p>如果<code>t!=NULL</code>,此函数返回值也会存储到<code>t</code>指向的内存。</p>
<pre><code>#include <stdio.h>
#include <time.h>
// unixtime.c
int main(void) {
time_t now = time(NULL);
if (now == -1) {
puts("The time() function failed");
}
printf("%ld\n", now);
return 0;
}
//❯ ./unixtime
//1638028257
</code></pre>
<h2 id="shi-jian-jie-gou-ti-struct-tm">时间结构体(struct tm)</h2>
<p>时间结构体是一个人类友好的日期时间。结构体类型为<code>struct tm</code>。函数<code>localtime()</code>可以将
Unix时间(time_t)
转换为时间结构体。他使用本地账户的时区。函数原型如下:</p>
<pre><code>struct tm *localtime(const time_t *clock);
</code></pre>
<p>函数存储了一个结构体并返回指针。</p>
<p>下面关于结构tm的描述来自FreeBSD手册。</p>
<pre><code>struct tm {
int tm_sec; /* seconds:0-59 */
int tm_min; /* minutes:0-59 */
int tm_hour; /* hours:0-23 */
int tm_mday; /* day of the month:1-31 */
int tm_mon; /* month:0-11 */
int tm_year; /* year:1900-> */
int tm_wday; /* day of the week:0-6 */
int tm_yday; /* day in the year:0-365 */
int tm_isdst; /* daylight saving time:是否实行夏令时 */
};
#include <stdio.h>
#include <time.h>
int main(void) {
time_t rawtime = time(NULL); // 得到Unix时间
if (rawtime == -1) {
puts("The time() function failed");
return 1;
}
struct tm *ptm = localtime(&rawtime); // 将Unix时间转换为时间结构体
if (ptm == NULL) {
puts("The localtime() function failed");
return 1;
}
printf("The time is: %02d:%02d:%02d\n", ptm->tm_hour,
ptm->tm_min, ptm->tm_sec);
return 0;
}
//❯ ./brokendowntime
//The time is: 17:00:12
</code></pre>
<h2 id="shi-jian-jie-gou-ti-zhuan-huan-wei-unixshi-jian">时间结构体转换为Unix时间</h2>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://zetcode.com/articles/cdatetime/">C Date time
(zetcode.com)</a>[原文]</li>
<li><a href="https://www.cnblogs.com/buptmuye/p/3711022.html">clock_gettime的使用,计时比clock()精确 - 木叶火影 - 博客园
(cnblogs.com)</a></li>
<li><a href="https://www.jianshu.com/p/0161e730eb5d">C语言getdate函数、setdate函数的用法 - 简书
(jianshu.com)</a></li>
</ol>
Coc-Clangd配置
2021-11-21T00:00:00+00:00
2021-11-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-613/
<p>使用vim进行c项目开发时,使用Coc-Clangd作为语言服务器实现自动补全。当对项目结构进行调整后(.h和.c文件分离到了不同的目录),Coc-Clangd需要一定的配置才能找到相关的.h文件,否则会出现错误:</p>
<pre><code>'*.h' file not found
[clang: pp_file_not_found]
</code></pre>
<p>具体需要在项目根目录生成<code>compile_commands.json</code>文件或者<code>compile_flags.txt</code>文件:</p>
<ol>
<li>当使用cMake时,可以运行<code>cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1</code>生成<code>compile_commands.json</code>。</li>
<li>当使用make时,需要安装<a href="https://github.com/rizsotto/Bear">Bear</a>(apt等包管理即可安装)。运行<code>make clean && bear make</code>即可生成<code>compile_commands.json</code>。</li>
<li>对于其它情况(只使用clang,没有构建工具和make)时。可以创建<code>compile_flags.txt</code>文件<a href="https://releases.llvm.org/8.0.0/tools/clang/tools/extra/docs/clangd/Installation.html#compile-flags-txt">参考2</a>进行相关配置。</li>
</ol>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://www.reddit.com/r/vim/comments/hrfm1q/cocvim_cocclangd_setup_help/">ProgrammAbel (u/ProgrammAbel) -
Reddit</a></li>
<li><a href="https://releases.llvm.org/8.0.0/tools/clang/tools/extra/docs/clangd/Installation.html">Getting started with clangd --- Extra Clang Tools 8 documentation
(llvm.org)</a></li>
</ol>
C语言库函数之函数间的goto
2021-11-14T00:00:00+00:00
2021-11-14T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-577/
<h2 id="setjmp">setjmp()</h2>
<p><a href="https://www.runoob.com/cprogramming/c-macro-setjmp.html">C 库宏 -- setjmp() | 菜鸟教程
(runoob.com)</a></p>
<pre><code>void longjmp(jmp_buf environment, int value)
</code></pre>
<p>返回0,等待<code>longjmp()</code>调用<code>setjmp()</code>宏执行处返回新值</p>
<h2 id="longjmp">longjmp()</h2>
<p><a href="https://www.runoob.com/cprogramming/c-function-longjmp.html">C 库函数 -- longjmp() | 菜鸟教程 (runoob.com)</a></p>
<pre><code>int setjmp(jmp_buf environment)
</code></pre>
<p>恢复最近一次调用<code>setjmp()</code>宏时保存的环境</p>
<p>setjmp()和longjmp()可以实现从一个函数直接跳转到另一个函数继续执行,类似于goto语句(但goto语句仅限于函数内使用)。不过这两个跳转并不保证堆栈正常,因此需要程序自己实现堆栈的保存与恢复(也就是保护现场和恢复现场)。通过sp和bp指针即可访问当前堆栈内容。堆栈寄存器sp(Stack
Pointer)一般用来存放栈的偏移指针;基数指针寄存器bp(Base
Pointer)一般在函数中用来保存进入函数时的sp的栈顶基址。bp到sp之间的内容即为当前函数的现场。在c语言中获取sp和bp指针的方法如下:</p>
<h2 id="signal">signal()</h2>
<p><a href="https://www.runoob.com/cprogramming/c-function-signal.html">C 库函数 -- signal() | 菜鸟教程 (runoob.com)</a></p>
<pre><code>void (*signal(int sig, void (*func)(int)))(int)
</code></pre>
<p>宏 信号</p>
<hr />
<p>SIGABRT (Signal Abort) 程序异常终止。
SIGFPE (Signal Floating-Point Exception) 算术运算出错,如除数为 0 或溢出(不一定是浮点运算)。
SIGILL (Signal Illegal Instruction) 非法函数映象,如非法指令,通常是由于代码中的某个变体或者尝试执行数据导致的。
SIGINT (Signal Interrupt) 中断信号,如 ctrl-C,通常由用户生成。
SIGSEGV (Signal Segmentation Violation) 非法访问存储器,如访问不存在的内存单元。
SIGTERM (Signal Terminate) 发送给本程序的终止请求信号。</p>
<p>sig参数</p>
<hr />
<p>SIG_DFL 默认的信号处理程序。
SIG_IGN 忽视信号。</p>
<hr />
<p>func参数</p>
<h2 id="alarm">alarm()</h2>
<p><a href="https://www.cnblogs.com/wuyepeng/p/9788919.html">alarm()函数的使用总结 - cs_wu - 博客园 (cnblogs.com)</a></p>
<pre><code>unsigned int alarm(unsigned int seconds);
</code></pre>
<p>设置一定时间后进行中断,执行signal()传入的函数。</p>
<p>signal函数和alarm函数可以通过中断来实现函数间的跳转,signal函数定义中断事件类型和中断发生时需要执行的函数;alarm函数定义多长时间后发生中断。</p>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://zhuanlan.zhihu.com/p/163852444">C语言(6)- 函数调用和栈 - 知乎 (zhihu.com)</a></li>
<li><a href="https://www.runoob.com/cprogramming/c-macro-setjmp.html">C 库宏 -- setjmp() | 菜鸟教程 (runoob.com)</a></li>
<li><a href="https://www.cnblogs.com/wuyepeng/p/9788919.html">alarm()函数的使用总结 - cs_wu - 博客园 (cnblogs.com)</a></li>
<li><a href="https://www.cnblogs.com/dongzhiquan/p/4960602.html">SS、SP、BP寄存器 - dzqdevin - 博客园 (cnblogs.com)</a></li>
</ol>
C语言标准输出格式化
2021-11-13T00:00:00+00:00
2021-11-13T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-584/
<p>c语言输出函数<code>printf</code>和<code>fprintf</code>使用修饰符格式化输出不同类型的变量。在这里总结一下各种不同的修饰符以及输出数据的格式。</p>
<h2 id="xiu-shi-fu">修饰符</h2>
<p>科学计数法</p>
<p>c语言输出修饰符</p>
<p>主要的修饰符如上表所示,而且可以在<code>%</code>和修饰符之间添加一个整型数字来指定输出内容的宽度,以及对齐方式。超出宽度的字符正常输出,负数表示左对齐:</p>
<p>另外针对整型和浮点型有特别的标识可以使用。</p>
<h2 id="zheng-xing">整型</h2>
<p>整型主要包括<code>short</code>,<code>int</code>,<code>long int</code>,<code>long long int</code>以及它们对应的无符号类型<code>unsigned short</code>,<code>unsigned int</code>,<code>unsigned long int</code>,<code>unsigned long long int</code>。整型可以以十进制(%d)、十六进制(%x)、八进制(%o)。</p>
<pre><code>int l = 31;
printf( "十进制(%%d)\t%d\n", l );
printf( "十六进制(%%x,%%X)\t%x,%X\n", l, l );
printf( "八进制(%%o)\t%o\n", l );
printf( "十进制(%%04d,%%4d)\t%04d,%4d\n", l, l );
printf( "十六进制(%%04x,%%4X) \t%04x,%4X\n", l, l );
printf( "八进制(%%o04o,%%4o)\t%04o,%4o\n", l, l );
/*
十进制(%d) 31
十六进制(%x,%X) 1f,1F
八进制(%o) 37
十进制(%04d,%4d) 0031, 31
十六进制(%04x,%4X) 001f, 1F
八进制(%o4o,%4o) 0037, 37
*/
</code></pre>
<h2 id="fu-dian-xing">浮点型</h2>
<p>浮点型主要包括<code>float</code>,<code>double</code>两种类型。可以使用%f,%g,%e进行输出。</p>
<p>其中%g和%s都以科学计数法的方式输出,不过%g在默认情况下包括整数位共输出六位,而%f和%e默认小数位数为六位。不过可以通过.m来指定小数点后面的位数。</p>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://zhuanlan.zhihu.com/p/206738818">C语言printf指定宽度的格式化输出 - 知乎 (zhihu.com)</a></li>
<li><a href="https://www.cnblogs.com/hustxujinkang/p/4179373.html">C/C++中浮点数输出格式问题 - sfesly - 博客园 (cnblogs.com)</a></li>
</ol>
SPI协议
2021-10-20T00:00:00+00:00
2021-10-20T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-566/
<p>串行外设接口(SPI)是一种同步串行通信接口规范,主要用于嵌入式系统的短距离通信。
该接口由摩托罗拉公司在20世纪80年代中期开发,已成为事实上的标准。
典型的应用包括闪存,EEPROM,SD卡和液晶显示器等。</p>
<p>有时SPI被称为四线串行总线,与三线、二线和一线串行总线形成对比。
SPI可以被准确地描述为一个同步串行接口,但它与同步串行接口(SSI)协议不同,后者也是一个四线同步串行通信协议。
SSI协议采用差分信号,只提供一个单工通信通道。
于此相对的是,SPI是一个主机和多个从机的通信。</p>
<p>SPI设备之间使用全双工模式通信,是一个主机和一个或多个从机的主从模式。主机负责初始化帧,这个数据传输帧可以用于读与写两种操作,片选线路可以从多个从机选择一个来响应主机的请求。</p>
<h2 id="jie-kou-ding-yi">接口定义</h2>
<p>SPI总线规定了4个保留逻辑信号接口:</p>
<ul>
<li>SCLK(Serial Clock):串行时钟,由主机发出</li>
<li>MOSI(Master Output, Slave
Input):主机输出从机输入信号(数据由主机发出)</li>
<li>MISO(Master Input, Slave
Output):主机输入从机输出信号(数据由从机发出)</li>
<li>SS(Slave Select):片选信号,由主机发出,一般是低电位有效</li>
</ul>
<p>SPI总线的通信操作可以在单个主设备与一或多个从机之间进行。在只有单一从机的情况下,如果从机允许,可以固定为低电平。大多数从机具有三态逻辑的特性,当器件未被选中时MISO信号变为高阻抗(逻辑断开)。没有三态输出的器件不能与其他器件共享SPI总线段,但是可以使用外接的三态逻辑缓存来解决这个问题。</p>
<p>SPI通信有4种不同的模式,不同的从设备可能在出厂就是配置为某种模式,这是无法改变的;但我们的通信双方必须工作在同一模式下,所以我们可以通过对CPOL(时钟极性)和CPHA(时钟相位)的配置来设置我们可以通过对CPOL(时钟极性)和CPHA(时钟相位)的配置来设置我们的主设备的工作模式;CPOL是用来配置SCLK的电平的空闲态或者有效态时的电平,CPHA是用来配置数据采样是发生在上升沿还是下降沿。具体如下:</p>
<ul>
<li>Mode0:CPOL=0,CPHA=0;空闲态时SCLK=0,数据在上升沿采样,在下降沿移位</li>
<li>Mode1:CPOL=0,CPHA=1;空闲态时SCLK=0,数据在下降沿采样,在上升沿移位</li>
<li>Mode2:CPOL=1,CPHA=0;空闲态时SCLK=1,数据在上升沿采样,在下降沿移位</li>
<li>Mode3:CPOL=1,CPHA=1;空闲态时SCLK=1,数据在下降沿采样,在上升沿移位</li>
</ul>
<div>
</div>
<p>四种模式</p>
<h2 id="shu-ju-chuan-shu-xian-zhi">数据传输限制</h2>
<p>SPI数据传输速率主要受限与三点:</p>
<ol>
<li>SPI的最大时钟频率</li>
<li>CPU处理SPI数据的能力</li>
<li>输出端驱动能力(PCB所允许的最大信号传输速率)</li>
</ol>
<h2 id="kuo-zhan">扩展</h2>
<p>一、SPI设备内部详细结构如图2所示,主要组件包括状态寄存器、控制寄存器和数据寄存器,数据位移逻辑单元,波特率发生器,主从控制逻辑单元和端口控制逻辑单元。</p>
<div>
</div>
<p>SPI设备内部详细结构图</p>
<p>二、由于在SPI
Flash的通信中,全双工并不常用,因此扩展了标准SPI中MOSI、MISO的用法,使其工作在半双工,用于加倍数据传输,这就形成了Dual
SPI;在Dual
SPI的基础上再增加两条信号线(SIO2、SIO3),可以在一个时钟内传输4bit数据,使传输速率再次加倍,就形成了Qual
SPI。</p>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="https://www.cnblogs.com/wcat/p/13854028.html">硬件设计:接口--SPI总线</a></li>
<li><a href="https://zh.wikipedia.org/wiki/%E5%BA%8F%E5%88%97%E5%91%A8%E9%82%8A%E4%BB%8B%E9%9D%A2">序列周邊介面</a></li>
</ol>
top命令笔记
2021-10-05T00:00:00+00:00
2021-10-05T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-376/
<h1 id="界面介绍">界面介绍</h1>
<h2 id="di-yi-xing-xin-xi-top">第一行信息(<code>top -</code>)</h2>
<table><thead><tr><th style="text-align: left">参数</th><th style="text-align: left">含义</th></tr></thead><tbody>
<tr><td style="text-align: left"><code>05:19:41</code></td><td style="text-align: left">系统当前时间</td></tr>
<tr><td style="text-align: left"><code>up 10 days, 20:15</code></td><td style="text-align: left">本机己经运行 10 天 20小时 15 分钟</td></tr>
<tr><td style="text-align: left"><code>1 users</code></td><td style="text-align: left">当前登录了一个用户</td></tr>
<tr><td style="text-align: left"><code>load average:0.01, 0.01, 0.00</code></td><td style="text-align: left">系统历史平均负载1分钟,5分钟,15分钟一般认为1表示一个CPU核满负载</td></tr>
</tbody></table>
<h2 id="di-er-xing-xin-xi-tasks">第二行信息(<code>Tasks: </code>)</h2>
<p>任务进程信息</p>
<table><thead><tr><th>参数</th><th>意义</th></tr></thead><tbody>
<tr><td><code>Tasks: 38 total</code></td><td>系统中的进程总数</td></tr>
<tr><td><code>1 running</code></td><td>正在运行的进程数</td></tr>
<tr><td><code>37 sleeping</code></td><td>睡眠的进程数</td></tr>
<tr><td><code>0 stoped</code></td><td>停止的进程数</td></tr>
<tr><td><code>0 zombie</code></td><td>僵尸进程数如果不是 0,则需要手工检查僵尸进程</td></tr>
</tbody></table>
<h2 id="di-san-xing-xin-xi-cpu-s">第三行信息(<code>%CPU(s)</code>)</h2>
<p>CPU状态信息,占用CPU百分比</p>
<table><thead><tr><th>参数</th><th>意义</th></tr></thead><tbody>
<tr><td>0.0 us</td><td>用户进程</td></tr>
<tr><td>0.0 sy</td><td>内核进程</td></tr>
<tr><td>0.0 ni</td><td>已调整优先级的用户进程</td></tr>
<tr><td>100.0 id</td><td>空闲</td></tr>
<tr><td>0.0 wa</td><td>等待IO</td></tr>
<tr><td>0.0 hi</td><td>处理硬件中断</td></tr>
<tr><td>0.0 si</td><td>处理软件中断</td></tr>
<tr><td>0.0 st</td><td>虚拟机</td></tr>
</tbody></table>
<h1 id="ming-ling-can-shu">命令参数</h1>
<h2 id="jiao-hu-ming-ling">交互命令</h2>
<table><thead><tr><th>命令</th><th>意义</th></tr></thead><tbody>
<tr><td>P</td><td>根据CPU使用百分比(%CPU)大小排序(默认状态)</td></tr>
<tr><td>M</td><td>根据内存使用量(%MEM)排序</td></tr>
<tr><td>T</td><td>根据时间(TIME)/累计时间(TIME+)排序</td></tr>
</tbody></table>
WSL2网络测试
2021-06-15T00:00:00+00:00
2021-06-15T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-396/
<h3 id="ce-shi-wang-luo-qing-kuang">测试网络情况</h3>
<p>WSL访问window中的127.0.0.1
127.0.0.1 失败
wsl网关 失败
WSL访问windows中的wsl网关
127.0.0.1 失败
wsl网关 成功
配置转发,在windows中将wsl网关转发到对应localhost端口</p>
<h3 id="pei-zhi-zhuan-fa">配置转发</h3>
<p>window可以使用<code>netsh interface portproxy</code>进行端口转发配置
添加转发</p>
<pre data-lang="powershell" class="language-powershell "><code class="language-powershell" data-lang="powershell">netsh interface portproxy add v4tov4 listenaddress=127.0.0.1 listenport=80 connectaddress=192.168.0.10 connectport=80
</code></pre>
<p>监听<code>127.0.0.1:80</code>并转发到<code>192.168.0.10:80</code></p>
<p>需要对防火墙进行配置
成功使用vim连接Joplin</p>
<p>windows重新启动后WSL2的IP地址会改变
在WSL内部利用<code>ip</code>命令获取主机IP</p>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">ip route | grep default | awk '{print $3}'
</code></pre>
<p>在windows中需要更改转发地址</p>
<pre data-lang="powershell" class="language-powershell "><code class="language-powershell" data-lang="powershell">wsl -d Linux -u root ip addr add 192.168.10.10/24 broadcast 192.168.50.255 dev eth0 label eth0:1
netsh interface ip add address "vEthernet (WSL)" 192.168.10.11 255.255.255.0
</code></pre>
<p>当重启windows时会重置此配置
写一个脚本,实现两个ip配置
使用windows计划任务实现脚本自动启动
创建任务是选择使用最高权限运行和隐藏</p>
<p>经常遇到由于<code>iphlpsvc</code>进程异常,导致的端口映射失败,重启此进程可解决
<code>iphlpsvc</code>经常异常,不知是何缘故
通过脚本重启</p>
<pre data-lang="powershell" class="language-powershell "><code class="language-powershell" data-lang="powershell">net stop iphlpsvc
net start iphlpsvc
</code></pre>
<h3 id="can-kao">参考</h3>
<p>1.<a href="https://www.v2ex.com/t/744955">终于找到给 wsl2 分配固定 ip 的方法</a></p>
C语言中的指针
2021-01-30T00:00:00+00:00
2021-01-30T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2021/wp-386/
<p>指针中相对复杂的主要有三类情况:指针数组、数组指针和函数指针,以及三者相互嵌套的情况。弄清楚C中各类符号的运算优先级才能真正理解其含义。</p>
<p>C语言运算符号优先级</p>
<table><thead><tr><th>优先级</th><th>运算符</th><th>结合律</th></tr></thead><tbody>
<tr><td>1</td><td>后缀运算符:<code>[],(),.,->,++,--</code></td><td>从左到右</td></tr>
<tr><td>2</td><td>一元运算符:<code>++,--,!,~,+,-,*,&,size_of</code></td><td>从右到左</td></tr>
<tr><td>3</td><td>类型转换运算:<code>(int)</code></td><td>从右到左</td></tr>
<tr><td>4</td><td>乘除运算符:<code>*,/,%</code></td><td>从左到右</td></tr>
<tr><td>5</td><td>加减运算符:<code>+,-</code></td><td>从左到右</td></tr>
<tr><td>6</td><td>移位运算符:<code><<,>></code></td><td>从左到右</td></tr>
<tr><td>7</td><td>关系运算符:<code><,<=,>,>=</code></td><td>从左到右</td></tr>
<tr><td>8</td><td>相等运算符:<code>==,!=</code></td><td>从左到右</td></tr>
<tr><td>9</td><td>位与运算符:<code>&</code></td><td>从左到右</td></tr>
<tr><td>10</td><td>位异或运算符:<code>^</code></td><td>从左到右</td></tr>
<tr><td>11</td><td>位或运算符:`</td><td>`</td></tr>
<tr><td>12</td><td>逻辑与运算符:<code>&&</code></td><td>从左到右</td></tr>
<tr><td>13</td><td>逻辑或运算符:`</td><td></td></tr>
<tr><td>14</td><td>条件运算符:<code>?:</code></td><td>从右到左</td></tr>
<tr><td>15</td><td>赋值运算符:`=,+=,-=,*=,/=,%=,&=,^=,</td><td>=,<<=,>>=`</td></tr>
<tr><td>16</td><td>逗号运算符:<code>,</code></td><td>从左到右</td></tr>
</tbody></table>
<p>运算符优先级和结合律</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c">int *p[3];
// [],*,int:数组,里面是指针,指向int
int (*p)[3];
// *,[],int:指针,指向数组,数组中是int
int (*p)(int a);
// *,(int a),int:指针,指向参数为(int a)的函数,函数返回int
int (*p[3])(int a);
// [],*,(int a),int:数组,数组中是指针,指向参数为(int a)的函数,函数返回int
int(*(*p())[])();
// (),*,[],*,(),int:无参函数,返回一个指针,指向一个数组,数组中是指针,指向一个无参函数,函数返回int
</code></pre>
<p>[]运算级比*高</p>
<h2 id="can-kao">参考</h2>
<ol>
<li><a href="http://c.biancheng.net/view/285.html">C语言运算符优先级</a></li>
<li><a href="https://www.jianshu.com/p/a6d5c9e3dd8c">C语言总结</a></li>
<li><a href="https://www.jianshu.com/p/30bfb7fbc3e8">C复杂指针</a></li>
</ol>
git基本用法笔记
2020-10-16T00:00:00+00:00
2020-10-16T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-351/
<h2 id="fen-zhi-jian-li-yu-he-bing">分支建立于合并</h2>
<pre><code>git commit # 提交
git branch branch_name # 新建分支
git checkout branch_name # 切换分支
git merge branch_name # 合并分支
# 将branch_name合并到当前分支,并增加一次commit
git rebase branch_name # 复制分支
# 将当前分支所有commit复制到branch_name,没有新增commit
# 当前分支和branch_name 有共同的祖先
</code></pre>
<h2 id="zai-fen-zhi-shu-shang-yi-dong">在分支树上移动</h2>
<p>branch_name指向分支树上的一个提交记录,每一个提交记录都有唯一的SHA-1
Hash值标识作为commit id</p>
<p>默认HEAD指向当前提交记录,当前记录有分支名,则显示为branch_name*</p>
<p>HEAD可以指向任何其他的提交记录,只需要checkout到一个指定的commit
id,或者使用引用</p>
<pre><code>git checkout da3423*
# 相对引用^
git checkout HEAD^^ # 将HEAD指向HEAD的第2父提交
git checkout master^ # 将HEAD指向master的第1父提交
git branch -f master HEAD^^^ # 强制将master指向HEAD的第三父提交
# 相对引用~
git checkout HEAD~10 # 将HEAD指向HEAD前10个commit id
# 撤销变更
git reset master^ # 当前分支指向master^,工作区内容不变
git revert master^ # 增加一次提交,使当前分支与master^相同
</code></pre>
<h2 id="yi-dong-ti-jiao-ji-lu">移动提交记录</h2>
<pre><code>git cherry-pick <提交号>...
# 将一系列提交copy到当前分支
git rebase -i <提交号>
# 以-i参数为基,重新整理提交(排序,删除等)
</code></pre>
matplotlib绘制x轴不连续的图
2020-10-15T00:00:00+00:00
2020-10-15T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-342/
<p>问题:matplotlib在绘制x轴不连续的数据时,会自补全;当使用日期作为横坐标时,会出现一些空值</p>
<p>例如:</p>
<pre><code>x = [1,2,3,4,5,8]
y = [1,4,9,16,25,36]
plt.plot(x,y)
</code></pre>
<div>
</div>
<p>matplotlib的x轴是一定连续的,因此先将需要的图形绘制出来,然后使用<code>plt.xticks(ticks,labels)</code>替换新的坐标显示</p>
<p>ticks为实际位置,labels为要显示的文本</p>
<p>画出需要的图形</p>
<pre><code>x = [1,2,3,4,5,8]
y = [1,4,9,16,25,36]
plt.plot(range(y),y)
</code></pre>
<div>
</div>
<p>重新定义x轴的显示</p>
<pre><code>plt.xticks(range(len(x)),x)
</code></pre>
<div>
</div>
在wsl上安装Arch Linux
2020-10-12T00:00:00+00:00
2020-10-12T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-326/
<p>首先启用"适用于 Linux 的 Windows
子系统"可选功能,并从应用商店安装Ubuntu(本文是在wsl1上进行实验的)</p>
<h2 id="bei-fen-xian-you-linuxfen-fa-ban">备份现有Linux分发版</h2>
<pre><code>wsl -l #查看所有Linux分发版
wsl --export <分发版> <文件名> #到处分发版到文件
</code></pre>
<h2 id="shi-yong-bootstrap-jing-xiang-an-zhuang">使用 Bootstrap 镜像安装</h2>
<p>下载Bootstrap镜像</p>
<ul>
<li><a href="https://www.archlinux.org/download/">https://www.archlinux.org/download/</a></li>
<li><a href="https://mirrors.tuna.tsinghua.edu.cn/archlinux/iso/">https://mirrors.tuna.tsinghua.edu.cn/archlinux/iso/</a></li>
</ul>
<p>选择合适版本的<code>archlinux-bootstrap-2020.10.01-x86_64.tar.gz</code>进行下载</p>
<p>解压</p>
<pre><code>tar -zxf archlinux-bootstrap-2020.10.01-x86_64.tar.gz
</code></pre>
<p>选择软件仓库服务器,去掉<code>etc/pacman.d/mirrorlist</code>中的一个注释</p>
<p>进入chroot</p>
<p>[进入失败]{.has-inline-color
.has-vivid-red-color},出现错误<code>mount: /tmp/root.x86_64/dev: unknown filesystem type 'devtmpfs'</code>和<code>ERROR: failed to setup chroot /tmp/root.x86_64</code></p>
<p>这是参照<a href="https://wiki.archlinux.org/index.php/Install_Arch_Linux_from_existing_Linux_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)" title="https://wiki.archlinux.org/index.php/Install_Arch_Linux_from_existing_Linux_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)">官网</a>的方法,可能不适用于wsl,参照<a href="https://github.com/yuk7/ArchWSL" title="https://github.com/yuk7/ArchWSL">github</a>安装一个新的Linux分发版Arch
Linux</p>
<h2 id="shi-yong-archwslan-zhuang-arch-linuxzi-xi-tong">使用ArchWSL安装Arch linux子系统</h2>
<p>ArchWSL是基于WSL的Arch linux分发版,专门用于管理Arch Linux分发版</p>
<p>Arch.exe用于Arch Linux子系统安装,其余两个应该是将Arch放到微软应用商店。</p>
<p>Note:安装.appx后开启Arch时出现<strong>系统资源不足,无法完成请求的服务。</strong></p>
<p>安装后是WSL2中的Arch Linux</p>
<p>Note:为了正常使用pacman需要<a href="https://github.com/yuk7/ArchWSL/wiki/How-to-Setup" title="https://github.com/yuk7/ArchWSL/wiki/How-to-Setup">初始化keyring</a></p>
<pre><code>sudo pacman-key --init
sudo pacman-key --populate
</code></pre>
<p>Note:安装systemctl alternative代替systemctl(WSL不支持systemctl)</p>
<p>WSL2之间安装<a href="https://aur.archlinux.org/packages/genie-systemd">genie-systemd</a>即可</p>
<p>Note:[使用<code>makepkg -si</code>命令时出错]{.has-inline-color
.has-vivid-red-color}</p>
<p><code>==> ERROR: Cannot find the strip binary required for object file stripping.</code></p>
<p>执行<code>sudo pacman -Sy base-devel</code>更新即可</p>
python标准库模块heapq
2020-10-11T00:00:00+00:00
2020-10-11T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-322/
<p>堆是一个近似完全二叉树的结构,并同时满足如下性质</p>
<blockquote>
<p><strong>子节点的键值或索引总是小于(或者大于)它的父节点</strong></p>
</blockquote>
<p>python标准库模块heapq实现了堆排序的一些算法,主要提供了以下几个函数</p>
<ol>
<li><code>heapify(x)</code>:本地把列表转换为堆,时间复杂度O(n)</li>
<li><code>heappush(heap, item)</code>:将新项添加到堆,并保持堆的不变性</li>
<li><code>heappop(heap)</code>:从堆中取出最小项,并保持堆的不变性</li>
<li><code>heapreplace(heap, item)</code>:从堆中取出最小项,并将新项添加到堆,并保持堆的不变性</li>
<li><code>heappushpop(heap, item)</code>:从堆中取出最小项,并将新项添加到堆,并保持堆的不变性(速度更快)</li>
<li><code>nsmallest(n, iterable, key=None)</code>:从可迭代对象中返回前n个最小项</li>
<li><code>nlargest(n, iterable, key=None)</code>:从可迭代对象中返回前n个最大项</li>
<li><code>merge(*iterables, key=None, reverse=False)</code>:合并多个可排序对象</li>
</ol>
初探Tornado
2020-08-30T00:00:00+00:00
2020-08-30T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-277/
<blockquote>
<p>Tornado是一种 Web 服务器软件的开源版本。Tornado 和主流Web
服务器框架(包括大多数 Python
的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。</p>
<p>得利于其非阻塞的方式和对epoll的运用,Tornado
每秒可以处理数以千计的连接,因此 Tornado 是实时 Web 服务的一个
理想框架。</p>
</blockquote>
<p>来自百度百科的解释,其中<strong>异步非阻塞</strong>和<strong>对epoll的运用</strong>是其两大特点</p>
<p>Tornado不同于<a href="http://docs.jinkan.org/docs/flask/index.html">Flask</a>和<a href="https://docs.djangoproject.com/en/3.1/" title="https://docs.djangoproject.com/en/3.1/">Django</a>两个常见框架,主要特点是Tornado内部实现了自己的Web服务器,而Flask和Django是采用WSGI协议与服务器对接。在开发时,Flask依赖<a href="https://werkzeug.palletsprojects.com/en/1.0.x/" title="https://werkzeug.palletsprojects.com/en/1.0.x/">Werkzeug</a>WSGI
工具集实现一个基于WSGI协议的内置web服务器来满足本地调试,部署的时候需要选择合适的<a href="http://docs.jinkan.org/docs/flask/deploying/index.html">WSGI服务器</a>来满足生产需求。Django也是同样原理,使用内置WSGI服务器满足开发,使用其他性能更高的WSGI服务器满足生产中的并发需求。(Tornado中的web服务器和web框架都可以实现对WSGI的兼容,可以Tornado服务器模块为其他web框架提供WSGI服务器,也可以使用其他服务器来部署Tornado的web应用。)</p>
<h2 id="yi-bu-fei-zu-sai">异步非阻塞</h2>
<p>异步和非阻塞是非常相关的而且这两个术语经常交换使用,但它们不完全相同。</p>
<h3 id="wei-shi-yao-yi-bu">为什么异步</h3>
<p>通常情况下,web服务器对与每个客户端的请求,都会创建一个线程与其连接,这就是同步模式。在处理高并发时,对每一个请求都会创建一个线程,如此多的线程开销是服务器的压力极大。异步模式在处理了并发请求时,先将请求放到后台处理,通过事件轮询等机制,将处理好的请求发送会客户端,从而减少了线程的创建。</p>
<h3 id="fei-zu-sai">非阻塞</h3>
<p>非阻塞是指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回(继续执行下面代码,或者使用重试机制)。阻塞的发生主要是由于<strong>系统IO</strong>(网络IO,文件读写IO等)引起的,或者在处理客户端请求时由于复杂的计算引起的CPU阻塞。总之阻塞的发生是由与服务器处理客户端请求需要一定的时间,直接执行(阻塞)或者直接返回,通过一定机制等到准备好要发送到客户端的内容后,发送到对应客户端。如何处理好系统IO是非阻塞的关键,Tornado是通过<strong>epoll</strong>方式的IO多路复用来实现的。</p>
<h2 id="epoll">epoll</h2>
<p>系统IO主要是程序与硬件间接交互的一种方式,如果每个程序都直接操作硬件,不仅程序开发会变得复杂,而且不同程序不统一的操作方式会使硬件混乱。因此有了操作系统,程序通过系统接口访问硬件,操作系统对所有硬件统一管理。当程序调用了系统的IO接口后,操作系统有不同的处理模式,不同的模式就是不同的IO模型。IO多路复用是一种IO模型,epoll是其中一种方式,常用的IO模型有五种:</p>
<ol>
<li>阻塞式IO<br />
当调用IO请求时直接进入<strong>阻塞状态</strong>,当IO完成(包括准备数据和拷贝数据)时,进入<strong>就绪状态</strong>,等待被再次调度进入<strong>运行状态</strong>。</li>
<li>非阻塞式IO<br />
当调用IO请求时,系统内核直接返回一个error信息,程序一边处理其它事情,每隔一段时间都询问内核IO数据是否准备完成,直到返回准备完成,然后把数据由内核拷贝到用户空间。</li>
<li>IO多路复用<br />
多路复用把一个程序的多个IO请求整合到一起进行阻塞,主要实现方式有select,poll,epoll。</li>
<li>信号驱动IO<br />
内核通过信号通知程序IO数据已经准备好。</li>
<li>异步IO<br />
发出IO请求后,内核把数据拷贝到用户空间后,通知程序IO已经完成。</li>
</ol>
<p>IO多路复用的方式</p>
<p>Tornado使用epoll(依赖依赖操作系统)的方式对网络IO进行多路复用,与客户端建立tcp连接后,能继续与其它客户端继续建立连接。接受完某客户端的数据后,调用对应的回掉函数处理。这样就能在一个线程里处理多个客户端的请求了。</p>
mysql 新建用户
2020-08-29T00:00:00+00:00
2020-08-29T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-270/
<p>一、新建用户</p>
<pre><code>CREATE USER 'username'@'host' IDENTIFIED BY 'password';
</code></pre>
<p>username:用户名</p>
<p>host:允许在那些ip登录(%)</p>
<p>password:密码</p>
<p>'username'和@和'host'之间没有空格</p>
<p>二、授权</p>
<pre><code>GRANT privileges ON databasename.tablename TO 'username'@'host';
FLUSH PRIVILEGES;
</code></pre>
<p>privileges:具体权限<code>SELECT</code>,<code>INSERT</code>,<code>UPDATE</code>,<code>DELETE</code>,<code>CREATE</code>,<code>DROP</code>(<code>ALL</code>)</p>
<p>databasename:数据库名(*)</p>
<p>tablename:表名(*)</p>
<p>三、撤权</p>
<p>与授权相似<code>GRANT</code>-><code>REVOKE</code>,<code>TO</code>-><code>FROM</code></p>
<pre><code>REVOKE privileges ON databasename.tablename FROM 'username'@'host';
FLUSH PRIVILEGES;
</code></pre>
<p>privileges:具体权限<code>SELECT</code>,<code>INSERT</code>,<code>UPDATE</code>,<code>DELETE</code>,<code>CREATE</code>,<code>DROP</code>(<code>ALL</code>)</p>
<p>databasename:数据库名(*)</p>
<p>tablename:表名(*)</p>
拼多多笔试之不同的骰子
2020-08-27T00:00:00+00:00
2020-08-27T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-197/
<h1 id="wen-ti">问题</h1>
<blockquote>
<p>输入:n个骰子的状态(上下左右前后)</p>
<p>输出:骰子的种类数和每种的个数</p>
</blockquote>
<p>相同的骰子:可以通过旋转达到相同状态的骰子<br />
例如:123456 == 563412</p>
<p>一、首先定义骰子的标准状态:</p>
<ol>
<li>1和2在相对面123 _ _ _</li>
<li>1和2在相邻面1_2 _ _ _</li>
</ol>
<p>二、接着要实现骰子的状态转变</p>
<ol>
<li>向前翻转90度</li>
<li>旋转90度</li>
</ol>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">def t1(x):
return [x[4],x[5],x[2],x[3],x[1],x[0]]
def t2(x):
return [x[0],x[1],x[5],x[4],x[2],x[3]]
</code></pre>
<p>三、将一个随机状态转变为指定状态</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">def t(x):
# 使1不在左右面
if x[2] == 1 or x[3] == 1:
x = t2(x)
while x[0]!=1:
x = t1(x)
tag = 2
if x[1] == 2:
tag = 3
while x[2]!=tag:
x = t2(x)
return x
</code></pre>
<p>对输入的序列调用上面的转换函数,然后就可以计算骰子的种类数和个数</p>
基于统计的分词方法
2020-08-06T00:00:00+00:00
2020-08-06T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-218/
<h2 id="nyuan-yu-yan-mo-xing-n-gram">n元语言模型(n-gram)</h2>
<p>假设$ S $表示长度为$ i $,由$ (w_1,w_2,\dots,w_m)$字序列组成的句子,则代表$ S $的概率为:</p>
<p>$$
P(S) =
P(w_1,w_2,\dots,w_m)=P(w_1)P(w_2|w_1)P(w_3|w_2,w_1)\cdots
P(w_i|w_1,w_2,\dots,w_{m-1})
$$</p>
<ul>
<li>$ n=1 $,uni-gram
$ P(w_1,w_2,\dots,w_m) = \prod_{i=1}^mP(w_i)$</li>
<li>$ n=2 $,bi-gram
$ P(w_1,w_2,\dots,w_m)
=\prod_{i=1}^mP(w_i|w_{i-1})$</li>
<li>$ n=3$,tri-gram
$ P(w_1,w_2,\dots,w_m)
=\prod_{i=1}^mP(w_i|w_{i-2}w_{i-1}) $</li>
</ul>
<h2 id="ji-yu-hmmde-fen-ci">基于HMM的分词</h2>
<h3 id="hmmde-can-shu">HMM的参数</h3>
<p>HMM的参观测序列(输出状态序列),状态序列(隐藏状态序列),初始概率,转移概率(转移概率矩阵),发射概率(发射概率矩阵)</p>
<p>状态值集合(隐藏状态):$ Q={q_1,q_2,\cdots,q_N}$,$
N$为可能的状态数,对应状态序列$ I$</p>
<p>观测值集合(输出状态):$ V={v_1,v_2,\cdots,v_M}$,$
M$为可能的观测数,对应观测序列$ O$</p>
<p>转移概率矩阵:$ A=[a_{ij}];i,j\in{1,2,\cdots,N}$,从$
i$状态到$ j$状态的转换概率($ \sum_{j=1}^Na_{ij}=1$)</p>
<p>发射概率矩阵(观测概率矩阵):$
B=[b_j(k)];j\in{1,2,\cdots,N},k\in{1,2,\cdots,M}$,从状态$
j$生成观测$ k$的概率</p>
<p>初始状态分布:$ \pi$</p>
<p>模型:$ \lambda=(A,B,\lambda)$,状态序列$
I$,观测序列$ O$</p>
<h3 id="hmmzhong-de-san-ge-wen-ti">HMM中的三个问题</h3>
<p><strong>概率计算问题</strong>:已知模型,求观测序列$ O$出现的概率;前向后向算法</p>
<p><strong>学习问题</strong>:已知观测序列,求模型参数,最大化$
P(O|\lambda )$;鲍姆-韦尔奇(Baum-Welch)算法</p>
<p>已知隐藏序列和观测序列(通过频数估计) $ A_{ij} $表示隐藏状态$ q_i $ 转移到$ q_j $的频率计数
$$ A=[a_{ij}];a_{ij}=\frac{A_{ij}}{\sum_{s=1}^NA_{is}}\ $$
$ B_{jk} $表示隐藏状态$ q_j $转移到观测状态
$ v_k $的频率计数
$$ B=[b_j(k)];b_j(k)=\frac{B_{jk}}{\sum_{s=1}^MB_{js}} $$
$ C(i)$为所有样本中初始隐藏状态$ q_j$的频率计数
$$ \Pi = \pi(i)=\frac{C(i)}{\sum_{s=1}^NC(s)} $$</p>
<p>仅知观测序列(鲍姆-韦尔奇算法,EM算法)
EM算法:最大似然估计用于没有隐变量的概率模型,EM算法可以用于有隐变量的算法模型
模型参数:
$$
\overline{\lambda} =
arg\;\max_{\lambda}\sum\limits_{I}P(I|O,\overline{\lambda})logP(O,I|\lambda)
$$</p>
<p><strong>解码问题</strong>:已知模型$ \lambda$与观测序列$
O$,求状态序列$ I$最大化$ P(I|O)$;维特比(Viterbi)算法</p>
<p>HMM是一种序列模型,不仅可以用到自然语言处理这个领域,其他领域的应用也很常见:<a href="http://tecdat.cn/%e7%94%a8%e6%9c%ba%e5%99%a8%e5%ad%a6%e4%b9%a0%e8%af%86%e5%88%ab%e4%b8%8d%e6%96%ad%e5%8f%98%e5%8c%96%e7%9a%84%e8%82%a1%e5%b8%82%e7%8a%b6%e5%86%b5-%e9%9a%90%e9%a9%ac%e5%b0%94%e7%a7%91%e5%a4%ab/">股指预测</a>,<a href="https://www.jianshu.com/p/16fc3712fdf6">语音识别</a>,<a href="http://www.c-a-m.org.cn/CN/abstract/abstract4637.shtml">网络安全</a>,<a href="https://arxiv.org/abs/1901.06286">基因序列</a>等方面,在自然语言处理中,HMM可以应用在分词,词性标注,命名实体识别等各个方面。</p>
<p>在分词方面可以这样理解HMM:</p>
<ul>
<li>观测序列(输出状态序列)---序列构成的句子或短文</li>
<li>状态序列(隐藏状态序列)---标注</li>
<li>初始概率---统计的第一个字序的概率</li>
<li>转移概率(转移概率矩阵)---第$ i$个字序到第$
i+1$个自序的标注变换概率</li>
<li>发射概率(发射概率矩阵)---从隐藏状态到输出状态的转换概率</li>
</ul>
<p>如果把观测序列看作标注,状态序列看作句子,从解码问题转变成学习问题会怎样? $
这种情况就成了判别模型,从给定的序列中提取特征,输出每个标签的概率,是直接拟合条件概率分布;而HMM是生成模型。</p>
vim及插件编译
2020-07-23T00:00:00+00:00
2020-07-23T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-169/
<h2 id="vimbian-yi">VIM编译</h2>
<h3 id="编译选项" class="has-text-align-left">编译选项</h3>
<p><code>--with-features=huge</code></p>
<p><code>--enable-multibyte</code></p>
<p><code>--enable-rubyiniterp</code></p>
<p><code>--enable-pythoninterp</code></p>
<p><code>--with-python-config-dir=/usr/lib/python2.7/config-x86_64-linux-gnu/</code></p>
<p><code>--enable-python3interp</code></p>
<p><code>--with-python3-config-dir=/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu/</code></p>
<p><code>--enable-luainterp</code></p>
<p><code>--enable-cscope</code></p>
<p><code>--enable-perlinterp</code></p>
<p><code>--prefix=~/.local</code></p>
<pre><code>./configure --with-features=huge --enable-multibyte --enable-rubyiniterp --enable-pythoninterp --with-python-config-dir=/usr/lib/python2.7/config-x86_64-linux-gnu/ --enable-python3interp --with-python3-config-dir=/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu/ --enable-luainterp --enable-cscope --enable-perlinterp --prefix=/home/frey/.local
make -j8
make install
</code></pre>
<h3 id="cuo-wu">错误</h3>
<p><code>You need to install a terminal library; for example ncurses</code><br />
安装libncurse5-dev可以解决</p>
<pre><code>apt install libncurse5-dev
</code></pre>
<h2 id="youcompletemebian-yi">YouCompleteMe编译</h2>
命名实体识别
2020-07-22T00:00:00+00:00
2020-07-22T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-179/
<blockquote>
<p>命名实体识别(英语:Named Entity
Recognition,简称NER),又称作专名识别、命名实体,是指识别文本中具有特定意义的实体,主要包括人名、地名、机构名、专有名词等,以及时间、数量、货币、比例数值等文字。指的是可以用专有名词(名称)标识的事物,一个命名实体一般代表唯一一个具体事物个体,包括人名、地名等。</p>
</blockquote>
<h1 id="早期方法" class="has-text-align-left">早期方法</h1>
<p>基于词典和规则的方法<br />
优点:快速、可解释性强和不需要太多标注语料<br />
缺点:需要领域知识、规则和词典的构建与维护繁重、未登录词匹配问题<br />
具体步骤:</p>
<ol>
<li>构建词典<br />
综合中文语义词库<a href="http://www.crsky.com/soft/31795.html">CSC</a>,<a href="http://www.keenage.com/html/e_index.html">hownet</a>,<a href="http://compling.hss.ntu.edu.sg/cow/">Chinese
Open Worldnet</a><br />
词典构建的统计方法:词频,TF-IDF,TextRank,借助百科,词法分析,依存句法分析<br />
构建词典可以通过人工,基于统计学的方法。主要目的就是分词和词性标注</li>
<li>规则构建<br />
规则构建是通过根据对词的标注(词性,特征),通过对标注设计一系列规则,从而得到规则中特定标注的实体类别。
<ol>
<li>基于词性标注进行规则推理<br />
通过特定的词性顺序,建立词性与实体的对应关系</li>
<li>采用一个特征集合来泛化表达命名实体<a href="https://www.researchgate.net/publication/220195686_Automatic_rule_learning_exploiting_morphological_features_for_named_entity_recognition_in_Turkish">Tatar and
Cicekli,2011</a><br />
为每个词生成特征,然后在特征上做规则的合并和构建。<br />
提出<a href="https://dl.acm.org/doi/10.1177/0165551511398573">一种自动学习规则的方案</a>,通过处理模式串的相似之处,通过泛化构建规则</li>
</ol>
</li>
<li>规则维护<br />
提出<a href="https://www.aclweb.org/anthology/P01-1055.pdf">一种基于机器学习长期维护规则的框架</a>,规则维护是在新增语料的情况下,通过对规则的改进与补充,提升其在新语料上的效果。</li>
</ol>
<h1 id="chuan-tong-ji-qi-xue-xi">传统机器学习</h1>
<h2 id="you-jian-du">有监督</h2>
<h3 id="svm-logistichui-gui-he-jue-ce-shu-deng">SVM,logistic回归和决策树等</h3>
<p>这类方法将NER视为分类问题,根据提取的特征和标注的类别对模型进行训练。捕获上下文关系能力弱,对分词结果要求高。</p>
<p>SVM(支持向量机)是在分类与回归分析中分析数据的监督式学习模型,主要思想是通过一个(p-1)维超平面将数据点(p维向量)分开,而且SVM可以通过核函数将低维线性不可分的数据集影射到高维空间使其线性可分。</p>
<p>决策树是一种概率模型,通过分支对样本特征进行划分,从而划分样本所属类别,决策树的生成主要是通过使用熵的概念。</p>
<h2 id="hmm-memm-crf"><a href="https://www.vhcffh.com/2019/%E5%9F%BA%E4%BA%8E%E7%BB%9F%E8%AE%A1%E7%9A%84%E5%88%86%E8%AF%8D%E6%96%B9%E6%B3%95/">HMM</a>,MEMM,CRF</h2>
<p>这些都属于序列模型,能够很好的捕获上下文关系。</p>
<p><strong>HMM</strong>模型主要用于序列标注问题,通过把标签看作隐藏状态变量,把要标注的序列看作可观察输出,可以有效的解决分词,词性标注,命名实体识别等问题。但是受限于HMM模型的两个假设,HMM只能学习到有限的序列特征。</p>
<ol>
<li>当前观测状态只与当前隐藏状态有关</li>
<li>当前隐藏状态只与前一时刻隐藏状态有关</li>
</ol>
<p><strong>MEMM</strong>(最大熵马尔可夫模型)是一种结合了HMM和最大熵模型的图模型,可用于序列标注问题。</p>
<p><strong>HMM</strong>将标注看作隐藏状态,不同的标注序列(隐藏状态)都可以计算出给定观测状态出现的概率,概率最大时对应的隐藏状态序列就是所求结果。而<strong>MEMM</strong>通过输入当前序列状态特征(这里可以加入多个特征,前后多个词或者标注),计算出每个标注的概率。也可以说是简单逻辑回归模型在序列模型上的扩展。</p>
<p>「在求解$latex P(X|Y)$时,可以直接求,也可以间接求$latex
P(Y|X)P(X)/P(Y)$」</p>
<p>在训练阶段,<strong>HMM</strong>通过概率估计(频率学派和贝叶斯派)获得模型参数,在预测阶段,可以通过维特比算法(一种dp算法,不用计算所有可能隐藏序列对应的观测序列概率)解码(找到最可能对应观测序列的隐藏状态序列)。<strong>MEMM</strong>直接通过定义特征,可以只将当前字符作为特征(此时就是logistic回归模型),也可以方便的加入任何其他特征(前后多个词,前面词的标注结果,或者后缀前缀等),通过对每个特征设置一个权重,代表其对最终结果的影响。通过训练,优化权重。预测时可以通过硬解码的方式一个一个给出类别(只考虑当前单词标签),另一种更好的方式是像HMM一样使用维特比算法求整个句子的最优解(CRF)。</p>
<h3 id="crf">CRF</h3>
<p>CRF模型与MEMM模型类似,也是通过序列特征计算每个token所有可能label的概率,取概率最大的label作为结果就是MEMM模型。这样计算出整个序列的结果后,可能出现不可理得标注(例如:[B,B...])。为了使输出序列前后相互约束,回想一下HMM的实现(当前观测状态依赖当前隐藏状态,当前隐藏状态依赖前一隐藏状态),因此可以考虑将当前label的概率与前一了label关联,求出整个序列所有可能label序列中可能性最大的。这样问题就变的复杂了,本来是sequence_lenth个分类问题(有多少种label,就有多少种类别),现在变成了一个分类问题,从所有可能label序列(共sequence_lenth^label_num个)选择合适的。</p>
<p>简单来说,CRF模型就是在MEMM模型的基础上增加了对输出的约束,使其输出的标签序列更加合理。</p>
<h1 id="shen-du-xue-xi">深度学习</h1>
<h2 id="you-jian-du-1">有监督</h2>
<ol>
<li>NN/CNN-CRF</li>
<li>Bi-LSTM-CRF</li>
</ol>
<h1 id="zui-xin-yan-jiu">最新研究</h1>
<h2 id="you-jian-du-2">有监督</h2>
<ol>
<li>Bi-LSTM-CRF+Attention</li>
</ol>
<h2 id="ban-jian-du">半监督</h2>
<ol>
<li>TagLM</li>
<li>迁移学习</li>
</ol>
<h1 id="can-kao">参考</h1>
<p>[1]<a href="https://easyai.tech/ai-definition/ner/">一文看懂命名实体识别 -
NER(发展史+4类方式+数据集+工具推荐)</a><br />
[2]<a href="https://zekizz.github.io/NER_survey_zeki.pdf">命名实体识别调研报告</a><br />
[3]<a href="https://carlos9310.github.io/2018/12/28/nlp-flow/">中文自然语言处理的一般流程</a><br />
[4]<a href="https://raw.githubusercontent.com/carlos9310/carlos9310.github.io/master/assets/pdf/chinese-nlp.pdf">中文自然语言处理的完整机器处理流程</a><br />
[5]<a href="https://homepages.inf.ed.ac.uk/csutton/publications/crftut-fnt.pdf">An Introduction to ConditionalRandom
Fields</a><br />
[6]<a href="https://zhuanlan.zhihu.com/p/60651380">最大熵马尔可夫模型</a></p>
confluent系列软件介绍
2020-07-21T00:00:00+00:00
2020-07-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-176/
<h2 id="zookeeper">zookeeper</h2>
<p>主要功能:配置管理、名字服务、分布式同步、集群管理
<a href="https://juejin.im/post/5ba879ce6fb9a05d16588802">集群配置</a>:每台机器的配置都相同,主要通过<code>myid</code>文件区分ip端口</p>
<h2 id="kafka"><a href="http://blog.vhcffh.com/2020/%e5%88%9d%e6%ad%a5%e4%ba%86%e8%a7%a3kafka/">kafka</a></h2>
<ol>
<li>
<p>Broker:Kafka的broker是无状态的,broker使用Zookeeper维护集群的状态。Leader的选举也由Zookeeper负责。</p>
</li>
<li>
<p>Zookeeper:Zookeeper负责维护和协调broker。当Kafka系统中新增了broker或者某个broker发生故障失效时,由ZooKeeper通知生产者和消费者。生产者和消费者依据Zookeeper的broker状态信息与broker协调数据的发布和订阅任务。</p>
</li>
<li>
<p>Producer:生产者将数据推送到broker上,当集群中出现新的broker时,所有的生产者将会搜寻到这个新的broker,并自动将数据发送到这个broker上。</p>
</li>
<li>
<p>Consumer:因为Kafka的broker是无状态的,所以consumer必须使用partition
offset来记录消费了多少数据。如果一个consumer指定了一个topic的offset,意味着该consumer已经消费了该offset之前的所有数据。consumer可以通过指定offset,从topic的指定位置开始消费数据。consumer的offset存储在Zookeeper中。</p>
</li>
</ol>
<h2 id="schema-registry">schema-registry</h2>
<p>作用:让 Kafka 支持 Avro
序列化,从而减少存储空间的占用,加快读取速度。大部分结构化的数据都有相同的schema,将结构和数据分开存储,减少数据量。
Avro序列化:将结构化数据变成有序的数据格式(一般是二进制),并通过schema记录数据结构。
生产者在使用schema-registry时首先向schema-registry注册数据格式,然后使用Avro序列化将数据发送到kafka;消费者消费数据时,先向schema-registry获取数据格式,然后从kafka的到数据,反序列化。
schema-registry服务端的数据存储在kafka中名为_schemas的topic
schema-registry提供了restful api
<a href="https://zhmin.github.io/2019/04/23/kafka-schema-registry/">参考</a></p>
<h2 id="kafka-rest">kafka-rest</h2>
<p>提供kafka restful api,实现通过http协议实现kafka
的生产者,消费者等。registry存储数据结构,提供了restful
api。kafka-rest也提供了api,用于存储数据。</p>
<h2 id="connect">connect</h2>
<p>Kafaka connect
是一种用于在Kafka和其他系统之间可扩展的、可靠的流式传输数据的工具。它使得能够快速定义将大量数据集合移入和移出Kafka的连接器变得简单。Kafka
Connect可以从数据库或应用程序服务器收集数据到Kafka
topic,使数据可用于低延迟的流处理。导出作业可以将数据从Kafka
topic传输到二次存储和查询系统,或者传递到批处理系统以进行离线分析。
用于其他数据库与kafka的连接,实现数据的导入导出。
<a href="https://www.cnblogs.com/laoqing/p/11927958.html">参考</a></p>
<h2 id="ksql-server">ksql-server</h2>
<p>实现kafka中数据的sql查询</p>
<h2 id="control-center">control-center</h2>
<p>confluent的控制中心,用于监控集群状态等。用商用版和社区版。</p>
<h2 id="confluent-ben-di-qi-dong">confluent 本地启动</h2>
<pre><code>$ ./bin/confluent local start
The local commands are intended for a single-node development environment
only, NOT for production usage. https://docs.confluent.io/current/cli/index.html
Using CONFLUENT_CURRENT: /tmp/confluent.HWNZqmRc
Starting zookeeper
zookeeper is [UP]
Starting kafka
kafka is [UP]
Starting schema-registry
schema-registry is [UP]
Starting kafka-rest
kafka-rest is [UP]
Starting connect
connect is [UP]
Starting ksql-server
ksql-server is [UP]
Starting control-center
control-center is [UP]
</code></pre>
<h2 id="confluent-ben-di-guan-bi">confluent 本地关闭</h2>
<pre><code>$ ./bin/confluent local stop
The local commands are intended for a single-node development environment
only, NOT for production usage. https://docs.confluent.io/current/cli/index.html
Using CONFLUENT_CURRENT: /tmp/confluent.HWNZqmRc
Stopping control-center
control-center is [DOWN]
Stopping ksql-server
ksql-server is [DOWN]
Stopping connect
connect is [DOWN]
Stopping kafka-rest
kafka-rest is [DOWN]
Stopping schema-registry
schema-registry is [DOWN]
Stopping kafka
kafka is [DOWN]
Stopping zookeeper
zookeeper is [DOWN]
</code></pre>
初步了解kafka
2020-07-19T00:00:00+00:00
2020-07-19T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-173/
<h2 id="kafkajian-jie">kafka简介</h2>
<p>kafka<a href="https://www.apache.org/dyn/closer.cgi?path=/kafka/0.9.0.0/kafka_2.11-0.9.0.0.tgz">下载地址</a>
kafka<a href="http://kafka.apache.org/">官网</a>
kafka是一个分布式消息发布订阅系统,主要特点:易于扩展,高吞吐量
<strong>主要术语:</strong></p>
<ol>
<li>Topic Kafka将消息种子(Feed)分门别类, 每一类的消息称之为话题(Topic).</li>
<li>Producer 发布消息的对象称之为话题生产者(Kafka topic producer)</li>
<li>Consumer
订阅消息并处理发布的消息的种子的对象称之为话题消费者(consumers)</li>
<li>Broker
已发布的消息保存在一组服务器中,称之为Kafka集群。集群中的每一个服务器都是一个代理(Broker).
消费者可以订阅一个或多个话题,并从Broker拉数据,从而消费这些已发布的消息。</li>
</ol>
<h2 id="qi-dong-fu-wu-duan">启动服务端</h2>
<ol>
<li>启动ZooKeeper,因为Kafka Cluster使用ZooKeeper
ZooKeeper是一个分布式服务框架,主要用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。</li>
</ol>
<!-- -->
<pre><code>bin/zookeeper-server-start.sh config/zookeeper.properties
</code></pre>
<ol start="2">
<li>启动Kafka Broker</li>
</ol>
<!-- -->
<pre><code>bin/kafka-server-start.sh config/server.properties
</code></pre>
<ol start="3">
<li>Jps查看进程</li>
</ol>
<!-- -->
<pre><code>$ jps
4407 QuorumPeerMain
4408 Kafka
5291 Jps
</code></pre>
<p>QuorumPeerMain是ZooKeeper守护进程,Kafka是Kafka守护进程</p>
<h2 id="chuang-jian-yi-ge-zhu-ti">创建一个主题</h2>
<pre><code>bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test
</code></pre>
<p><code>replication-factor</code>分区个数,不同分区会存储到不同的主机(负载均衡),分区写入策略(<em>1、轮询策略</em>,2、随机策略,3、按键保存策略)
<code>partitions</code>副本个数,仅用于数据冗余</p>
<h2 id="huo-qu-zhu-ti-lie-biao">获取主题列表</h2>
<pre><code>bin/kafka-topics.sh --list --bootstrap-server localhost:9092
</code></pre>
<h2 id="sheng-chan-zhe-shi-li">生产者示例</h2>
<pre><code>bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic test
</code></pre>
<p><code>bootstrap-server</code>代理列表,kafka服务端配置文件中指定</p>
<h2 id="xiao-fei-zhe-shi-li">消费者示例</h2>
<pre><code>bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 —topic topic-name --from-beginning
</code></pre>
<p><code>from-beginning</code>读取历史未消费的数据</p>
一些有用的python函数
2020-05-14T00:00:00+00:00
2020-05-14T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-160/
<h1 id="ji-chu-han-shu">基础函数</h1>
<h2 id="map">map</h2>
<pre><code>map(function, iterable, ...)
</code></pre>
<p>对迭代器每一给元素进行计算<br />
返回新的迭代器</p>
<h2 id="filter">filter</h2>
<pre><code>filter(function, iterable)
</code></pre>
<p>过滤掉不满足function的函数<br />
返回过滤后的迭代器</p>
<h2 id="heapqmo-kuai">heapq模块</h2>
<p>参考python中文文档<a href="https://docs.python.org/zh-cn/3/library/heapq.html">https://docs.python.org/zh-cn/3/library/heapq.html</a></p>
<p>模块提供了一系列的函数用来操作list,从而实现最小堆的特性</p>
<pre><code>import heapq
heapq.heapify(x) #原地转化为最小堆
heapq.heappush(heap,item) #堆中加入新项
heapq.heappop(heap) #弹出堆中最小项
heapq.heapreplace(heap,item) #弹出堆中最小项并加入新项
</code></pre>
<h1 id="functoolsmo-kuai-zhong-de-han-shu">functools模块中的函数</h1>
<h2 id="reduce">reduce</h2>
<pre><code>from functools import reduce
reduce(function, sequence, startValue)
</code></pre>
<p>将一个序列归纳为一个输出<br />
返回一个输出</p>
<h2 id="lru-cache">lru_cache</h2>
<pre><code>from functools import lru_cache
@lru_cache(user_function)
@lru_cache(maxsize=128, typed=False)
</code></pre>
<p>装饰器缓存函数的输出值<br />
maxsize:最多缓存最近多少个返回值<br />
typed:类型是否作为缓存中键的一部分(如果为True,3和3.0两次缓存)</p>
集合合并问题
2020-05-12T00:00:00+00:00
2020-05-12T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-155/
<blockquote>
<p>题目: 小明有n只袜子,需要穿m天 第i只袜子的颜色为c_i
给出每天要穿的两只袜子的编号(i1,i2)
保证每天穿的袜子颜色一样,最少要对多少只袜子进行染色</p>
</blockquote>
<p>思路:
根据这m天每天穿的两只袜子,可以将所有袜子分成几个集合,每个集合的颜色都要一样,把集合中所有袜子染成颜色最多的袜子即可
将每天穿的袜子标号看作集合,按袜子颜色进行合并,直到每个集合中袜子的标号都不重复
实现函数</p>
<pre><code>def maxTint(c,d):
"""
c: n只袜子的颜色
d: m天每天穿的袜子的标号
"""
pass
</code></pre>
<p>1,循环标号进行合并</p>
<pre><code>def maxTint(c,d):
d = map(set,d)
for ci in range(len(c)):
new_d = []
new_g = []
for di in d:
if ci in di:
new_g.append(di)
else:
new_d.append(di)
d = new_d
if len(new_g)!=0:
d.append(set())
for gi in new_g:
d[-1]=d[-1]|gi
ans = 0
for di in d:
ansi = 0
for i in di:
ansi=max(ansi,c.count(c[i-1]))
ans+=ansi
return ans
</code></pre>
<p>时间复杂度:<code>n*m</code></p>
python的整除
2020-05-11T00:00:00+00:00
2020-05-11T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-151/
<p>python可以通过<code>//</code>进行整除运算 例如:</p>
<pre><code>4//2
# 2
5//2
# 2
# 去尾法
</code></pre>
<p>当遇到负数时,最后的结果向下取整数 此时负号运算级高,先构成负数,再除法</p>
<pre><code>-4//2
# -2
-5//2
# -3
-1//2
# -1
</code></pre>
<p>如果是减号,就先计算除法</p>
<pre><code>-1//2
# -1
1-1//2
# 1
</code></pre>
<p>python远算符优先级</p>
<p>运算符 描述</p>
<hr />
<p><code>**</code> 指数
<code>~ + -</code> 按位翻转, 一元加号和减号(就是正数和负数)
<code>* / % //</code> 乘,除,取模和取整除
<code>+ -</code> 加法和减法
<code>>> <<</code> 右移,左移
<code>&</code> 位与运算
<code>^ |</code> 位异或,位或
<code><= < > >=</code> 比较远算
<code>== !=</code> 等于远算
<code>= %= /= //= -= += *= **=</code> 赋值
<code>is is not</code> 身份
<code>in not in</code> 成员
<code>and or not</code> 逻辑与或非</p>
<p><code>-</code>负号比<code>//</code>整除远算级高</p>
数独的解法
2020-04-08T00:00:00+00:00
2020-04-08T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-143/
<h1 id="shu-du-de-gui-ze">数独的规则</h1>
<p>在9×9的方格里面填写数字1-9,满足</p>
<ul>
<li>每行不重复</li>
<li>每列不重复</li>
<li>9个3×3的小方格不重复</li>
</ul>
<h1 id="si-lu">思路</h1>
<h2 id="hui-su-fa">回溯法</h2>
<p>通过DFS遍历尝试每一个数字
根据三条规则,可以用三个二维数组记录每行,每列和每个小方格可填数字状态</p>
<pre><code>row = [[True]*9 for _ in range(9)]
col = [[True]*9 for _ in range(9)]
block = [[True]*9 for _ in range(9)]
</code></pre>
<p><code>row[i][j]</code>表示第<code>i</code>行是否可以填数字<code>j</code>
<code>col[i][j]</code>表示第<code>i</code>列是否可以填数字<code>j</code>
<code>block[i][j]</code>表示第<code>i</code>个方格是否可以填数字<code>j</code></p>
<p>用二维数组<code>g</code>表示数独方格的原始状态,<code>0</code>表示空白</p>
<pre><code>def dfs():
# 遍历所有位置
for i in range(9):
for j in range(9):
# 空白位置
if g[i][j]==0:
# 尝试每一个数字
for k in range(9):
# 数字满足行,列,块
if row[i][k] and col[j][k] and block[3*(i//3)+j//3][k]:
# 填入数字
row[i][k] = col[j][k] = block[3*(i//3)+j//3][k]= False
g[i][j] = k+1
if(dfs()):
return True
# 有填错的,删除数字
row[i][k] = col[j][k] = block[3*(i//3)+j//3][k]= True
g[i][j] = 0
# 九个数字都不能填
return False
# 所有位置都填了数字
return True
</code></pre>
<p>回溯法要注意如何进行记录状态,要方便更新,回溯</p>
<h1 id="can-kao-zi-liao">参考资料</h1>
<p>[1]<a href="https://blog.csdn.net/xiaoyiaoyou/article/details/52491187">数独求解算法(回溯法和唯一解法)java实现</a></p>
tensorflow2建立模型的方法
2020-04-06T00:00:00+00:00
2020-04-06T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-137/
<h1 id="1-shi-yong-input">1.使用Input</h1>
<pre><code>tf.keras.Input(shape=None, batch_size=None, name=None, dtype=None, sparse=False, tensor=None, ragged=False, **kwargs)
</code></pre>
<p><code>Input</code>初始化一个占位符 <code>shape</code>:指定模型输入的shape,不包括batch size
<code>batch_size</code>:可选 <code>name</code>:可选,图层名称 <code>dtype</code>:字符串,数据类型
<code>sparse</code>:布尔,是否稀疏 <code>tensor</code>:可选,指定tensor,不再是占位符</p>
<pre><code>from tensorflow.keras import Input, Model
from tensorflow.keras.layers import Dense
x = Input(shape=(2,))
y = Dense(5, activation='softmax')(x)
model = Model(x, y)
</code></pre>
<h1 id="2-ji-cheng-model">2.继承Model</h1>
<pre><code>import tensorflow as tf
class MyModel(tf.keras.Model):
def __init__(self):
super(MyModel, self).__init__()
self.dense = tf.keras.layers.Dense(5, activation=tf.nn.softmax)
def call(self, inputs):
x = self.dense1(inputs)
return self.dense(x)
model = MyModel()
</code></pre>
<h1 id="3-shi-yong-sequential">3.使用Sequential</h1>
<pre><code>import tensorflow as tf
model1 = tf.keras.Sequential(
tf.keras.layers.Dense(5, activation=tf.nn.softmax)
)
</code></pre>
栈实现四则远算
2020-03-26T00:00:00+00:00
2020-03-26T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-122/
<h2 id="zong-jie">总结</h2>
<p>四则运算表达式求值分两步<br />
中缀转后缀,栈中存符号<br />
后缀算结果,栈中存数字</p>
<h2 id="zhong-zhui-biao-da-shi-yu-hou-zhui-biao-da-shi-zhuan-huan">中缀表达式与后缀表达式转换</h2>
<p>中缀表达式<code>1+(2-3)*4+5/6</code><br />
后缀表达式<code>1,2,+</code><br />
通过栈进行转换</p>
<ul>
<li>数字:直接输出</li>
<li>(:进栈</li>
<li>):出栈直到(,弹出(</li>
<li>运算符:出栈(高阶等阶运算符),进栈(当前运算符)</li>
</ul>
<!-- -->
<pre><code>#当前字符:1+(2-3)*4+5/6
#输出:1
#栈:
#当前字符:+(2-3)*4+5/6
#输出:1
#栈:+
#当前字符:(2-3)*4+5/6
#输出:1
#栈:+,(
#当前字符:2-3)*4+5/6
#输出:1,2
#栈:+,(
#当前字符:-3)*4+5/6
#输出:1,2
#栈:+,(,-
#当前字符:3)*4+5/6
#输出:1,2,3
#栈:+,(,-
#当前字符:)*4+5/6
#输出:1,2,3,-
#栈:+
#当前字符:*4+5/6
#输出:1,2,3,-,
#栈:+(低阶不弹出),*
#当前字符:4+5/6
#输出:1,2,3,-,4
#栈:+,*
#当前字符:+5/6
#输出:1,2,3,-,4,*(高阶弹出),+(等阶弹出)
#栈:+
#当前字符:5/6
#输出:1,2,3,-,4,*,+,5
#栈:+
#当前字符:/6
#输出:1,2,3,-,4,*,+,5
#栈:+,/
#当前字符:6
#输出:1,2,3,-,4,*,+,5,6
#栈:+,/
#当前字符:
#输出:1,2,3,-,4,*,+,5,6,/,+
#栈:
#1+(2-3)*4+5/6
</code></pre>
<h2 id="hou-zhui-biao-da-shi-de-ji-suan">后缀表达式的计算</h2>
<p>数字进栈,符号远算</p>
<pre><code>#[1,[[[2,3,-],4,*],[5,6,/],+],+]
#2-3=-1
#-1*4=-4
#5/6=0.833
#-4+0.833=-3.167
#1+(-3.167)=-2.167
</code></pre>
<h2 id="pythonshi-xian">python实现</h2>
<pre><code>def calculator(s: str) -> float:
s = s.replace(" ", "")
s_list = []
for c in list(s):
if c.isdigit():
if len(s_list) == 0 or isinstance(s_list[-1], str):
s_list.append(int(c))
else:
s_list[-1] *= 10+int(c)
else:
s_list.append(c)
# 中缀转后缀
stack = []
s_end = []
for c in s_list:
if isinstance(c, int):
s_end.append(c)
elif c == "(":
stack.append(c)
elif c == ")":
while stack[-1] != "(":
s_end.append(stack.pop())
stack.pop()
elif c in ["*", "/"]:
while len(stack) != 0 and stack[-1] in ["*", "/"]:
s_end.append(stack.pop())
stack.append(c)
elif c in ["+", "-"]:
while len(stack) != 0 and stack[-1] in ["+", "-", "*", "/"]:
s_end.append(stack.pop())
stack.append(c)
else:
continue
while stack:
s_end.append(stack.pop())
print(s_end)
# 计算后缀表达式
stack = []
for c in s_end:
if isinstance(c, int):
stack.append(c)
elif c == "+":
stack[-2] += stack[-1]
stack.pop()
elif c == "-":
stack[-2] -= stack[-1]
stack.pop()
elif c == "*":
stack[-2] *= stack[-1]
stack.pop()
elif c == "/":
stack[-2] /= stack[-1]
stack.pop()
else:
continue
return stack[0]
if __name__ == "__main__":
print(calculator("1+(2-3)*4+5/6"))
</code></pre>
<h2 id="leetcodexiang-guan-ti-mu">leetcode相关题目</h2>
<p>1.<a href="https://leetcode.com/problems/basic-calculator/">https://leetcode.com/problems/basic-calculator/</a><br />
2.<a href="https://leetcode.com/problems/basic-calculator-ii/">https://leetcode.com/problems/basic-calculator-ii/</a><br />
3.<a href="https://leetcode.com/problems/basic-calculator-iii/">https://leetcode.com/problems/basic-calculator-iii/</a><br />
4.<a href="https://leetcode.com/problems/basic-calculator-iv/">https://leetcode.com/problems/basic-calculator-iv/</a></p>
linux交换分区
2020-03-24T00:00:00+00:00
2020-03-24T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-102/
<h2 id="xin-jian-ying-pan-fen-qu-zuo-wei-jiao-huan-fen-qu">新建硬盘分区作为交换分区</h2>
<p>硬盘分区情况</p>
<pre><code>设备 起点 末尾 扇区 大小 类型
/dev/sda1 2048 821247 819200 400M Linux 文件系统
/dev/sda2 821248 1640447 819200 400M Linux 文件系统
/dev/sda3 1640448 840501247 838860800 400G Linux 文件系统
/dev/sda4 840501248 871958527 31457280 15G VMware 诊断
</code></pre>
<p>把<code>/dev/sda4</code>作为交换分区 交换分区大小建议</p>
<p>物理内存 不需要休眠 需要休眠 最大值</p>
<hr />
<p>256M 256M 512M 512M
512M 512M 1024M 1024M
1024M 1024M 2048M 2048M
1G 1G 2G 2G
2G 1G 3G 4G
3G 2G 5G 6G
4G 2G 6G 8G
5G 2G 7G 10G
6G 2G 8G 12G
8G 3G 11G 16G
12G 3G 15G 24G
16G 4G 20G 32G
24G 5G 29G 48G
32G 6G 38G 64G
64G 8G 72G 128G
128G 11G 139G 256G</p>
<p>archlinux的wiki中建议交换分区类型为82 安装交换分区<code>mkswap /dev/sda4</code>
启用交换分区<code>swapon /dev/sdxy</code> 设置自动启动
在文件<code>/etc/fstab</code>中添加<code>UUID=<UUID> none swap defaults 0 0</code>
其中UUID<code>lsblk -no UUID /dev/sda4</code></p>
<h2 id="can-kao-zi-liao">参考资料</h2>
<p>[1]<a href="https://segmentfault.com/a/1190000008125116">Linux交换空间(swap
space)</a> [2]<a href="https://wiki.archlinux.org/index.php/Swap_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#%E4%BA%A4%E6%8D%A2%E6%96%87%E4%BB%B6">Swap
(简体中文)</a></p>
vim中的leetcode插件
2020-03-24T00:00:00+00:00
2020-03-24T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-111/
<h2 id="an-zhuang">安装</h2>
<p>1.安装依赖</p>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">pip install keyring browser-cookie3
</code></pre>
<p>2.使用<code>vundle</code>安装并简单配置</p>
<pre><code>call vundle#begin()
...
Plugin 'ianding1/leetcode.vim'
...
call vundle#end()
"leetcode"
let g:leetcode_china=1 "中国区leetcode"
let g:leetcode_solution_filetype='python3' "默认使用python3"
let g:leetcode_browser='chrome' "登录leetcode-cn.com的浏览器"
</code></pre>
<h2 id="wen-ti">问题</h2>
<ol>
<li><code>leetcode.vim</code>依赖<code>keyring</code>
按照<a href="https://github.com/ianding1/leetcode.vim#faq">这里</a>配置使用GnomeKeyring</li>
</ol>
<pre><code>[backend]
default-keyring=keyring.backends.Gnome.Keyring
</code></pre>
<p>vim中执行<code>:LeetCodeSignIn</code>出现错误<code>browser_cookie3 not installed</code>
明明已经安装过了</p>
<ol start="2">
<li>
<p>通过<code>python</code>直接导入<code>browser_cookie3</code>
发现原因<code>ModuleNotFoundError: No module named 'keyring.backends.Gnome'</code></p>
</li>
<li>
<p>查看<code>keyring</code>的<a href="https://github.com/jaraco/keyring">资料</a>后,发现<code>keyring</code>把<code>keyring.backends.Gnome.Keyring</code>单独放到了<code>keyrings.alt</code>包中
安装<code>keyrings.alt</code>,有提示<code>RuntimeError: GnomeKeyring module required</code>
<code>GnomeKeyring</code>是<a href="https://pygobject.readthedocs.io/en/latest/getting_started.html">PyGObject</a>里面的,<a href="https://pygobject.readthedocs.io/en/latest/getting_started.html">PyGObject</a>是<code>Gtk</code>python接口,在archlinux中需要安装<code>pygobject-devel</code>和<code>libgnome-keyring</code></p>
</li>
<li>
<p><del><code>browser-cookie3</code>无法正确获取<code>chrome</code>的cookies,只能获取到key,获得的value为空,猜想应该是<code>browser-cookie3</code>解密cookies时出现了问题。原因就是<code>chrome</code>更改了对<code>cookie</code>的加密方法,因此<code>browser-cookie3</code>无法获取<code>cookie</code>。
通过更改<code>leetcode.vim</code>使用<code>pycookiecheat</code>库获取<code>cookie</code>也可以。</del></p>
</li>
<li>
<p><del>更新<code>browser-cookie3</code>库也可以解决这个问题<a href="https://github.com/borisbabic/browser_cookie3/pull/52">Pull</a></del></p>
</li>
<li>
<p><code>browser-cookie3</code>库可以正常使用了。
因此可以尝试从git直接安装browser-cookie3这个库</p>
</li>
</ol>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">pip install git+https://github.com/borisbabic/browser_cookie3.git
</code></pre>
<h2 id="can-kao-zi-liao">参考资料</h2>
<ol>
<li><a href="https://github.com/ianding1/leetcode.vim">leetcode.vim插件</a></li>
<li><a href="https://github.com/jaraco/keyring">keyring</a></li>
<li><a href="https://github.com/n8henrie/pycookiecheat">pycookiecheat</a></li>
</ol>
vim命令记录
2020-03-15T00:00:00+00:00
2020-03-15T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-60/
<h1 id="zhe-die-dai-ma">折叠代码</h1>
<p>vim中共有六种代码折叠方式,不同的折叠方式有不同的折叠命令</p>
<ul>
<li>manual //手工定义折叠</li>
<li>indent //用缩进表示折叠</li>
<li>expr //用表达式来定义折叠</li>
<li>syntax //用语法高亮来定义折叠</li>
<li>diff //对没有更改的文本进行折叠</li>
<li>marker //用标志折叠</li>
</ul>
<h2 id="indent">indent</h2>
<p>配置方法</p>
<pre><code># ~/.vimrc
set foldmethod=indent
</code></pre>
<p>使用indent方式,vim会利用自动缩进进行折叠代码</p>
<h3 id="quan-ju-zhe-die">全局折叠</h3>
<p>关闭折叠 打开折叠</p>
<hr />
<p><code>zm</code>关闭一层折叠 <code>zr</code>打开一层折叠
<code>zM</code>关闭多层折叠 <code>zR</code>打开多层折叠</p>
<h3 id="dan-xing-zhe-die">单行折叠</h3>
<p>关闭折叠 打开折叠</p>
<hr />
<p><code>zc</code>关闭一层折叠 <code>zo</code>打开一层折叠
<code>zC</code>关闭多层折叠 <code>zO</code>打开多层折叠</p>
<h3 id="kuai-su-yi-dong">快速移动</h3>
<p>后退 前进</p>
<hr />
<p><code>[z</code>折叠开始 <code>]z</code>折叠末尾
<code>zk</code>上一折叠 <code>zj</code>下一折叠</p>
<h1 id="yi-xie-qi-ta-ming-ling">一些其他命令</h1>
<h2 id="zi-dong-huan-xing-deng-wen-ti">自动换行等问题</h2>
<p>设置行宽<code>:set textwidth=78</code> 删除换行符<code>gq</code>,<code>J</code> 自动折行(默认)<code>:set wrap</code>
取消自动折行<code>:set nowrap</code></p>
<h1 id="can-kao-zi-liao">参考资料</h1>
<p>[1]<a href="http://vimcdoc.sourceforge.net/doc/index.html">http://vimcdoc.sourceforge.net/doc/index.html</a></p>
实现让终端走代理
2020-03-13T00:00:00+00:00
2020-03-13T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/wp-41/
<p>让终端走代理可以通过设置环境变量实现</p>
<pre><code>export http_proxy=socks5://127.0.0.1:1080
export https_proxy=socks5://127.0.0.1:1080
export ALL_PROXY=socks5://127.0.0.1:1080
</code></pre>
<p>把代理服务器的配置写到shell配置文件<code>~/.bashrc</code>或者<code>./zshrc</code><br />
并通过<code>alias</code>实现方便的设置代理<code>setproxy</code>,关闭代理<code>unsetproxy</code>,验证代理<code>myip</code></p>
<pre><code># ~/.zshrc
# setproxy,unsetproxy,myip
alias setproxy="export ALL_PROXY=socks://localhost:1080"
alias unsetproxy="unset ALL_PROXY"
alias myip="curl http://cip.cc"
</code></pre>
<p>验证ip通过获取本机外网ip实现[2]
<img src="https://i.imgur.com/Xtdho3W.png" alt="file" /></p>
<h1 id="wen-ti">问题</h1>
<p>本机使用v2ray开的<code>socks</code>代理,设置代理用<code>sock5</code>会出现错误,只能设置成socks,可能是终端不支持socks5,v2ray兼容socks<br />
查<a href="https://www.v2ray.com/chapter_02/protocols/socks.html">资料</a>得知<code>v2ray</code>中的<code>socks</code>兼容
Socks 4、Socks 4a 和 Socks 5<br />
<code>curl: (6) Couldn't resolve host name</code><br />
但是在chrome中使用SwitchyOmega设置为<code>sock5</code>可以上网</p>
<h1 id="can-kao-zi-liao">参考资料</h1>
<p>[1]<a href="https://blog.fazero.me/2015/09/15/%E8%AE%A9%E7%BB%88%E7%AB%AF%E8%B5%B0%E4%BB%A3%E7%90%86%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E6%B3%95/" title="让终端走代理的几种方法">让终端走代理的几种方法</a><br />
[2]<a href="https://www.cnblogs.com/wjoyxt/p/6288582.html" title="用Linux命令行获取本机外网IP地址">用Linux命令行获取本机外网IP地址</a><br />
[3]<a href="https://www.v2ray.com/chapter_02/protocols/socks.html">www.v2ray.com</a></p>
数列求解总结
2020-02-06T00:00:00+00:00
2020-02-06T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2020/hexo-shu-lie-qiu-jie-zong-jie/
<p><a href="https://www.desmos.com/calculator/akwh6q3dhr">网址</a></p>
<p><img src="https://blog.vhcffh.com/2020/hexo-shu-lie-qiu-jie-zong-jie/index.png" alt="图片" /></p>
Transformers
2019-12-21T00:00:00+00:00
2019-12-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-transformers/
<p><a href="https://github.com/huggingface/transformers">transformers</a>(与<em>pytorch-transformers</em>和<em>pytorch-pretrained-bert</em>相似)是python的一个库,它提供了用于自然语言理解(NLU)和自然语言生成(NLG)的多种预训练模型(BERT,GPT-2,RoBERTa,XLM,DistilBert,XLNet.....),为100多种语言提供了超过32种的预训练模型,并实现Tensorflow
2.0和Pytorch的深度互操作。</p>
<p>特点</p>
<ul>
<li>与pytorch-transformers一样容易使用</li>
<li>如Keras一样强大简洁</li>
<li>在NLU和NLG任务上有很好的表现</li>
<li>容易学习</li>
</ul>
<p>模型</p>
<ol>
<li><a href="https://github.com/google-research/bert">BERT</a> (from Google)
released with the paper <a href="https://arxiv.org/abs/1810.04805">BERT: Pre-training of Deep Bidirectional
Transformers for Language
Understanding</a> by Jacob Devlin,
Ming-Wei Chang, Kenton Lee and Kristina Toutanova.</li>
<li><a href="https://github.com/openai/finetune-transformer-lm">GPT</a> (from
OpenAI) released with the paper <a href="https://blog.openai.com/language-unsupervised">Improving Language Understanding by
Generative
Pre-Training</a> by Alec
Radford, Karthik Narasimhan, Tim Salimans and Ilya Sutskever.</li>
<li><a href="https://blog.openai.com/better-language-models">GPT-2</a> (from
OpenAI) released with the paper <a href="https://blog.openai.com/better-language-models">Language Models are Unsupervised
Multitask Learners</a>
by Alec Radford*, Jeffrey Wu*, Rewon Child, David Luan, Dario Amodei
<strong>and Ilya Sutskever</strong>.</li>
<li><a href="https://github.com/kimiyoung/transformer-xl">Transformer-XL</a> (from
Google/CMU) released with the paper <a href="https://arxiv.org/abs/1901.02860">Transformer-XL: Attentive
Language Models Beyond a Fixed-Length
Context</a> by Zihang Dai*, Zhilin
Yang*, Yiming Yang, Jaime Carbonell, Quoc V. Le, Ruslan
Salakhutdinov.</li>
<li><a href="https://github.com/zihangdai/xlnet">XLNet</a> (from Google/CMU)
released with the paper <a href="https://arxiv.org/abs/1906.08237">XLNet: Generalized Autoregressive
Pretraining for Language
Understanding</a> by Zhilin Yang*,
Zihang Dai*, Yiming Yang, Jaime Carbonell, Ruslan Salakhutdinov,
Quoc V. Le.</li>
<li><a href="https://github.com/facebookresearch/XLM">XLM</a> (from Facebook)
released together with the paper <a href="https://arxiv.org/abs/1901.07291">Cross-lingual Language Model
Pretraining</a> by Guillaume Lample
and Alexis Conneau.</li>
<li><a href="https://github.com/pytorch/fairseq/tree/master/examples/roberta">RoBERTa</a>
(from Facebook), released together with the paper a <a href="https://arxiv.org/abs/1907.11692">Robustly
Optimized BERT Pretraining
Approach</a> by Yinhan Liu, Myle Ott,
Naman Goyal, Jingfei Du, Mandar Joshi, Danqi Chen, Omer Levy, Mike
Lewis, Luke Zettlemoyer, Veselin Stoyanov.</li>
<li><a href="https://huggingface.co/transformers/model_doc/distilbert.html">DistilBERT</a>
(from HuggingFace) released together with the paper <a href="https://arxiv.org/abs/1910.01108">DistilBERT, a
distilled version of BERT: smaller, faster, cheaper and
lighter</a> by Victor Sanh, Lysandre
Debut and Thomas Wolf. The same method has been applied to compress
GPT2 into
<a href="https://github.com/huggingface/transformers/tree/master/examples/distillation">DistilGPT2</a>.</li>
<li><a href="https://github.com/pytorch/fairseq/tree/master/examples/ctrl">CTRL</a>
(from Salesforce), released together with the paper <a href="https://www.github.com/salesforce/ctrl">CTRL: A
Conditional Transformer Language Model for Controllable
Generation</a> by Nitish
Shirish Keskar*, Bryan McCann*, Lav R. Varshney, Caiming Xiong and
Richard Socher.</li>
<li><a href="https://huggingface.co/transformers/model_doc/camembert.html">CamemBERT</a>
(from FAIR, Inria, Sorbonne Université) released together with the
paper <a href="https://arxiv.org/abs/1911.03894">CamemBERT: a Tasty French Language
Model</a> by Louis Martin, Benjamin
Muller, Pedro Javier Ortiz Suarez, Yoann Dupont, Laurent Romary,
Eric Villemonte de la Clergerie, Djame Seddah, and Benoît Sagot.</li>
<li><a href="https://github.com/google-research/ALBERT">ALBERT</a> (from Google
Research), released together with the paper a <a href="https://arxiv.org/abs/1909.11942">ALBERT: A Lite BERT
for Self-supervised Learning of Language
Representations</a> by Zhenzhong Lan,
Mingda Chen, Sebastian Goodman, Kevin Gimpel, Piyush Sharma, Radu
Soricut.</li>
<li><a href="https://github.com/pytorch/fairseq/tree/master/examples/xlmr">XLM-RoBERTa</a>
(from Facebook AI), released together with the paper <a href="https://arxiv.org/abs/1911.02116">Unsupervised
Cross-lingual Representation Learning at
Scale</a> by Alexis Conneau*,
Kartikay Khandelwal*, Naman Goyal, Vishrav Chaudhary, Guillaume
Wenzek, Francisco Guzmán, Edouard Grave, Myle Ott, Luke Zettlemoyer
and Veselin Stoyanov.</li>
</ol>
<p>使用</p>
<p>对于每个模型,这个库主要涉及3个类</p>
<ul>
<li>model classes:提供模型的结构,如:<code>BertModel</code></li>
<li>configuration classes:存储构建模型的所有参数,如<code>BertConfig</code></li>
<li>tokenizer
classes:提供模型的词汇表和用于对字符串解码/编码的方法,如<code>BertTokenizer</code></li>
</ul>
<p>所有的实例可以通过<code>from_pretrained()</code>加载预训练模型,通过<code>save_pretrained()</code>保存模型</p>
<p>BERT example</p>
<p>从字符串获得输入向量</p>
<pre><code>import torch
from transformers import BertTokenizer, BertModel, BertForMaskedLM
# OPTIONAL: if you want to have more information on what's happening under the hood, activate the logger as follows
import logging
logging.basicConfig(level=logging.INFO)
# Load pre-trained model tokenizer (vocabulary)
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
# 中文bert模型'bert-base-chinese'
# bert 特殊的符号
# '[MASK]' 用于mask language model
# '[CLS]' 开头,用于分类的标记
# '[PAD]'
# '[SEP]' 结尾,句子分隔符
# '[UNK]'
# Tokenize input
text = "[CLS] Who was Jim Henson ? [SEP] Jim Henson was a puppeteer [SEP]"
tokenized_text = tokenizer.tokenize(text)
# Mask a token that we will try to predict back with `BertForMaskedLM`
masked_index = 8
tokenized_text[masked_index] = '[MASK]'
assert tokenized_text == ['[CLS]', 'who', 'was', 'jim', 'henson', '?', '[SEP]', 'jim', '[MASK]', 'was', 'a', 'puppet', '##eer', '[SEP]']
# Convert token to vocabulary indices
indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text)
# Define sentence A and B indices associated to 1st and 2nd sentences (see paper)
segments_ids = [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
# Convert inputs to PyTorch tensors
tokens_tensor = torch.tensor([indexed_tokens])
segments_tensors = torch.tensor([segments_ids])
</code></pre>
<p>对输入进行编码</p>
<pre><code># Load pre-trained model (weights)
model = BertModel.from_pretrained('bert-base-uncased')
# Set the model in evaluation mode to deactivate the DropOut modules
# This is IMPORTANT to have reproducible results during evaluation!
model.eval()
# If you have a GPU, put everything on cuda
tokens_tensor = tokens_tensor.to('cuda')
segments_tensors = segments_tensors.to('cuda')
model.to('cuda')
# Predict hidden states features for each layer
with torch.no_grad():
# See the models docstrings for the detail of the inputs
outputs = model(tokens_tensor, token_type_ids=segments_tensors)
# Transformers models always output tuples.
# See the models docstrings for the detail of all the outputs
# In our case, the first element is the hidden state of the last layer of the Bert model
encoded_layers = outputs[0]
# We have encoded our input sequence in a FloatTensor of shape (batch size, sequence length, model hidden dimension)
assert tuple(encoded_layers.shape) == (1, len(indexed_tokens), model.config.hidden_size)
</code></pre>
<p>使用<code>BertForMaskedLM</code>预测masked token</p>
<pre><code># Load pre-trained model (weights)
model = BertForMaskedLM.from_pretrained('bert-base-uncased')
model.eval()
# If you have a GPU, put everything on cuda
tokens_tensor = tokens_tensor.to('cuda')
segments_tensors = segments_tensors.to('cuda')
model.to('cuda')
# Predict all tokens
with torch.no_grad():
outputs = model(tokens_tensor, token_type_ids=segments_tensors)
predictions = outputs[0]
# confirm we were able to predict 'henson'
predicted_index = torch.argmax(predictions[0, masked_index]).item()
predicted_token = tokenizer.convert_ids_to_tokens([predicted_index])[0]
assert predicted_token == 'henson'
</code></pre>
<p>参考</p>
<p>1.<a href="https://huggingface.co/transformers/index.html">https://huggingface.co/transformers/index.html</a></p>
机器学习笔记
2019-11-15T00:00:00+00:00
2019-11-15T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-ji-qi-xue-xi-bi-ji/
<h2 id="fen-lei">分类</h2>
<p>准确率:所有样本中预测正确的占比</p>
<p>$$
accuracy =\frac {TP+TN}{TP+TN+FP+FN} = \frac {T}{T+F}
$$</p>
<p>精确率:<strong>预测为正的样本</strong>中真正的正样本占比</p>
<p>$$
precision = \frac {TP}{TP+FP} = \frac {TP}{P'}
$$</p>
<p>召回率:<strong>正样本</strong>中预测为正的占比</p>
<p>$$
recall = \frac{TP}{TP+FN} = \frac{TP}{P}
$$</p>
<p>F1:精确率和召回率的<strong>调和均值</strong></p>
<p>$$
\begin{align*}
\frac{2}{F_1} & = \frac{1}{precision} + \frac{1}{recall}\cr
F_1 & = \frac{2\cdot precision \cdot recall}{precision+recall}\cr
F_1 & = \frac{2TP}{2TP + FP + FN} \cr
F_1 & = \frac{2TP}{P' + P} \cr
\end{align*}
$$</p>
<p>F-score:</p>
<p>$$
F_{score}=(1+\beta^2)\cdot \frac{precision \cdot recall}{\beta^2\cdot precision + recall}
$$</p>
<table><thead><tr><th></th><th>P</th><th>N</th></tr></thead><tbody>
<tr><td>P'</td><td>TP</td><td>FP</td></tr>
<tr><td>N'</td><td>FN</td><td>TN</td></tr>
</tbody></table>
<h2 id="xu-lie">序列</h2>
<p>BLEU(Bilingual Evaluation understudy)</p>
<p>$$
CP_n(C,S)=\frac {\sum_i\sum_k\min(h_k(c_i),max_{j \in m}h_k(s_{ij}))}{\sum_i\sum_kh_k(c_j)}
$$</p>
<p><strong>惩罚因子BP(Brevity Penalty)</strong></p>
<p>$$
b(C,S)=\begin{cases}
1, &l_c \lt l_s \cr
e^{1-\frac{l_s}{l_c}}, &l_c \geq l_s
\end{cases}
$$</p>
<p>$$
BLEU_N(C,S)=b(C,S)\exp(\sum_{n=1}^N\omega_n\log CP_n(C,S))
$$</p>
<p>机器翻译</p>
<p>ROUGE(Recall-Oriented Understudy for Gisting Evaluation)</p>
<table><thead><tr><th>ROUGE-N</th><th>基于N-gram公现性统计</th></tr></thead><tbody>
<tr><td>ROUGE-L</td><td>基于最长公有子句共现性精确度和召回率Fmeasure统计</td></tr>
<tr><td>ROUGE-W</td><td>代权重的最长公有子句共现性精确度和召回率Fmeasure统计</td></tr>
<tr><td>ROUGE-S</td><td>不连续二元组共现性精确度和召回率Fmeasure统计</td></tr>
</tbody></table>
<p><strong>ROUGE-N</strong></p>
<p>$$
ROUGE-N=\frac {\sum_{S \in ReferencesSummaries}\sum_{gram_n\in S}Count_{match}(gram_n)}
{\sum_{S \in ReferencesSummaries}\sum_{gram_n\in S}Count(gram_n)}
$$</p>
<p><strong>ROUGE-L</strong>
最长公共子句longest common subsequence(LCS)</p>
<p>$$
R_{lcs}=\frac {LCS(X,Y)}{m} ,m=len(X)
$$</p>
<p>$$
P_{lcs}=\frac {LCS(X,Y)}{n} ,n=len(Y)
$$</p>
<p>$$
F_{lcs}=\frac{(1+\beta^2)R_{lcs}P_{lcs}}{R_{lcs}+\beta^2P_{lcs}}
$$</p>
交叉熵损失的反向传播
2019-10-02T00:00:00+00:00
2019-10-02T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-jiao-cha-shang-sun-shi-de-fan-xiang-chuan-bo/
<p>对于一个单标签多分类问题,假设网络的输出层的输入为$Z_{in}=[z_1,\cdots,z_i,\cdots,z_n]$,
输出为$\hat Y=[\hat y_1,\cdots,\hat y_i,\cdots,\hat y_n]$,
真实类标签为$Y = [y_1,\cdots,y_i,\cdots,y_n]$,$n$为类别数(输出层神经元数),通常有:</p>
<p>$$
\hat Y = Softmax(Z_{in})\tag{1}
$$</p>
<p>$$
\hat y_i = \frac {e^{z_i}}{\sum_{j=0}^n e^{z_j}}\tag{2}
$$</p>
<p>其中$Softmax$为:</p>
<p>$$
Softmax(Z_{in}) = [\cdots,\frac {e^{z_i}}{\sum_{j=1}^n e^{z_j}},\cdots]\tag{3}
$$</p>
<h2 id="jiao-cha-shang-sun-shi">交叉熵损失</h2>
<p>$$
Loss(Y,\hat Y) = -\sum_{i=1}^ny_i*\ln(\hat y_i)\tag{4}
$$</p>
<p>损失对神经网络输出的偏导(<a href="https://www.vhcffh.com/2019/%E7%9F%A9%E9%98%B5%E4%B8%AD%E7%9A%84%E6%B1%82%E5%AF%BC/">标量对向量求偏导</a>)为:</p>
<p>$$
\frac {\partial Loss(Y,\hat Y)}{\partial \hat Y} = [-\frac {y_1}{\hat y_1},\cdots,-\frac {y_i}{\hat y_i},\cdots,-\frac {y_n}{\hat y_n}]\label{5}\tag{5}
$$</p>
<p>后向传播推导中遇到的所有量都是变量,最终的目的是找到损失关于某变量的偏导,程序中也只用这个公式求得对应输入点的梯度</p>
<h2 id="softmaxde-pian-dao">Softmax的偏导</h2>
<p>求$\hat y_i$对$z_i$的偏导,根据{2}可得:</p>
<p>求$\hat Y$对$Z$的偏导(<a href="https://blog.vhcffh.com/hexo-ju-zhen-zhong-de-qiu-dao/">向量对向量求导</a>)</p>
<p>这里把$y$的坐标写作$k$</p>
<p>$k=i$时:</p>
<p>$$
\begin{split}
\frac {\partial \hat y_i}{\partial z_i}
&= e^{z_i} * \frac {1}{\sum_{j=1}^ne^{z_j}} +
e^{z_i} * (-\frac {1}{(\sum_{j=1}^n e^{z_j})^2}) * e^{z_i} \cr
&= \frac {e^{z_i}}{\sum_{j=1}^n e^{z_j}} - (\frac {e^{z_i}}{\sum_{j=1}^n e^{z_j}})^2 \cr
&= \hat y_i - \hat y_i^2
\end{split}
\tag{6}
$$</p>
<p>$k \neq i$时:</p>
<p>$$
\begin{split}
\frac {\partial \hat y_k}{\partial z_i}
&= e^{z_k} * (-\frac {1}{(\sum_{j=1}^n e^{z_j})^2}) * e^{z_i} \cr
&= -\frac {e^{z_k}*e^{z_i}}{(\sum_{j=1}^n e^{z_j})^2} \cr
&= - \hat y_k\hat y_i
\end{split}
\tag{7}
$$</p>
<p>写成矩阵:</p>
<p>$$
\frac {\partial \hat Y}{\partial Z_{in}} =
\begin{bmatrix}
\hat y_1-\hat y_1^2 & -\hat y_2\hat y_1 & \cdots & -\hat y_n\hat y_1\cr
-\hat y_1\hat y_2 & \hat y_2-\hat y_2^2 & \cdots & -\hat y_n\hat y_2\cr
\vdots & \vdots & \ddots & \vdots\cr
-\hat y_1\hat y_n & -\hat y_2\hat y_n & \cdots & \hat y_n-\hat y_n^2\cr
\end{bmatrix}\tag{8}
$$
这是一个对称矩阵,在链式求导时加不加转置都一样</p>
<p>根据(5)和(8),损失$L$对输入$Z$的偏导(<a href="https://blog.vhcffh.com/hexo-ju-zhen-zhong-de-qiu-dao/">标量对向量求偏导</a>):</p>
<p>$$
\begin{align}
\frac {\partial L(Y,\hat Y)}{\partial Z}
&= \frac {\partial L(Y,\hat Y)}{\partial \hat Y} (\frac {\partial \hat Y}{\partial Z})^T\cr
&= [-\frac {y_1}{\hat y_1},\cdots,-\frac {y_i}{\hat y_i},\cdots,-\frac {y_n}{\hat y_n}]
\begin{bmatrix}
\hat y_1-\hat y_1^2 & -\hat y_2\hat y_1 & \cdots & -\hat y_n\hat y_1\cr
-\hat y_1\hat y_2 & \hat y_2-\hat y_2^2 & \cdots & -\hat y_n\hat y_2\cr
\vdots & \vdots & \ddots & \vdots\cr
-\hat y_1\hat y_n & -\hat y_2\hat y_n & \cdots & \hat y_n-\hat y_n^2\cr
\end{bmatrix}^T\cr
&= [(\hat y_1-1)y_1+\hat y_1y_2+\cdots+\hat y_1y_n,\hat y_2y_1+(\hat y_2-1)y_2+\cdots+\hat y_1y_n,\cdots]\cr
&= [\hat y_1\sum_{i=1}^ny_i-y_1,\cdots,\hat y_j\sum_{i=1}^ny_i-y_j,\cdots,\hat y_n\sum_{i=1}^ny_i-y_n]\cr
&= [\hat y_1-y_1,\cdots,\hat y_j-y_j,\cdots,\hat y_n-y_n]\ \ \ \ (\sum_{i=1}^ny_i=1)\cr
&= \hat Y-Y
\end{align}
$$</p>
矩阵中的求导
2019-10-02T00:00:00+00:00
2019-10-02T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-ju-zhen-zhong-de-qiu-dao/
<h3 id="biao-liang-dui-xiang-liang-qiu-dao">标量对向量求导</h3>
<p>$$
y = f(x_1,\cdots,x_i,\cdots,x_n)
$$</p>
<p>$$
X = [x_1,\cdots,x_i,\cdots,x_n]
$$</p>
<p>$$
\frac {\partial y}{\partial X} = [\frac {\partial f}{\partial x_1},\cdots,\frac {\partial f}{\partial x_i},\cdots,\frac {\partial f}{\partial x_n}]
$$</p>
<h3 id="xiang-liang-dui-xiang-liang-qiu-dao">向量对向量求导</h3>
<p>$$
Y = [f_1(x_1,\cdots,x_i,\cdots,x_n),\cdots,
f_i(x_1,\cdots,x_i,\cdots,x_n),\cdots,
f_m(x_1,\cdots,x_i,\cdots,x_n)]
$$</p>
<p>$$
X = [x_1,\cdots,x_i,\cdots,x_n]
$$</p>
<p>$$
\frac {\partial Y}{\partial X} =
\begin{bmatrix}
\frac {\partial f_1}{\partial x_1} & \frac {\partial f_2}{\partial x_1} & \cdots & \frac {\partial f_m}{\partial x_1}\cr
\frac {\partial f_1}{\partial x_2} & \frac {\partial f_2}{\partial x_2} & \cdots & \frac {\partial f_m}{\partial x_1}\cr
\vdots & \vdots & \ddots & \vdots\cr
\frac {\partial f_1}{\partial x_n} & \cdots & \cdots & \frac {\partial f_m}{\partial x_n}\cr
\end{bmatrix}
$$</p>
<p>这是一个n行m列的矩阵,有时也会写成m行n列,都是一样的,区别在于加不加转置</p>
日常笔记-1
2019-09-24T00:00:00+00:00
2019-09-24T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-ri-chang-bi-ji-1/
<p>pickle序列化与反序列化</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> import pickle as plk
plk.dump(obj,file) # 将序列化后的二进制写入文件
plk.dumps(obj) # 返回一个二进制序列
plk.load(file) # 读文件对象中的二进制,转化成对象返回
plk.loads(bytes_object) # 将二进制序列转化成对象
obj1=dict(),obj2=dict()
plk.dump(obj1,file)
plk.dump(obj2,file)
obj1=plk.load(file)
obj2=plk.load(file)
plk.load(file) # EOFError: Ran out of input
</code></pre>
<p>文本分类与关键词排名</p>
<p>文本分类(Text Categorization, TC)</p>
<p>特征-倒文本频率(Term-Inverse Document Frequency, TF-IDF)</p>
<p>信息增益(Information Gain, IG)</p>
<p>互信息(Multi-Information, MI)</p>
<p>卡方统计 (Chi-square, CHI)</p>
<p>期 望 交 叉 熵(Expected Cross Entropy, ECE )</p>
<p>文本证据权( Weight of Evidence for Text, WET)</p>
<p>TextRank(与PageRank的原理相同)</p>
<p>HITS(Hyperlink-Induced Topic Search)</p>
<p>卡方统计模型(CHI)</p>
<p>卡方分布</p>
<p>其中$Z_i \sim N(0,1)$(标准正太分布)</p>
<p>则$X$被称为服从自由度为$k$的卡方分布,记作:</p>
<p>卡方分布的概率密度函数:</p>
<p>期望和方差:</p>
<p>模型通过观察值和理论值的偏差来确定理论是否正确</p>
<p>基于词袋模型(文档由词组成,不考虑词的顺序)</p>
<p>通过考虑词语与类别的相关度把由词语组成的文档归为某一类别</p>
<p>HITS</p>
<ul>
<li>一个高质量的权威页面会被很多高质量的枢纽页面所指向。</li>
<li>一个高质量的枢纽页面会指向很多高质量的权威页面。</li>
</ul>
<p>由PageRank算法演变而来,将当前页面出链也做为考虑当前页面的重要性的一个因素</p>
<p>参考</p>
<p><a href="https://github.com/wangjiang0624/Note/blob/master/MachineLearning/%E6%96%87%E6%9C%AC%E5%88%86%E7%B1%BB.md">https://github.com/wangjiang0624/Note/blob/master/MachineLearning/%E6%96%87%E6%9C%AC%E5%88%86%E7%B1%BB.md</a></p>
<p><a href="http://blog.zhengyi.one/PageRank-HITS.html">http://blog.zhengyi.one/PageRank-HITS.html</a></p>
nvidia显卡驱动错误
2019-09-09T00:00:00+00:00
2019-09-09T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-nvidiaxian-qia-qu-dong-cuo-wu/
<p>系统使用<code>bumblebee</code>实现双显卡(N卡和集成显卡)切换,<br />
同时有N卡的开源驱动nouveau和专有显卡驱动nvidia</p>
<p>使用命令</p>
<pre><code>optirun glxspheres64
# 或者optirun glxspher
</code></pre>
<p>出现错误<code>[XORG] (EE) Failed to load module "nouveau"</code><br />
通过命令<code>lsmod |grep nouveau</code><br />
显示模块已经加载</p>
<p>通过<code>/etc/modprobe.d/blacklist.conf</code>文件禁用<code>nouveau</code>(需要重建内核<code>mkinitcpio -P</code>并重启),但<code>nouveau</code>驱动仍然加载了,<code>nvidia</code>未加载</p>
<p>解决方法修改文件<code>/etc/bumblebee/xorg.conf.nouveau</code><br />
去掉<code>BusID "PCI:01:00:0"</code>的注释<br />
<code>PCI:01:00:0</code>通过命令<code>lspci | egrep (3D|VGA)</code></p>
<p>重启后<code>nvidia</code>驱动加载成功,<code>nouveau</code>没有加载</p>
基于统计的分词方法
2019-09-08T00:00:00+00:00
2019-09-08T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-ji-yu-tong-ji-de-fen-ci-fang-fa/
<h2 id="1-nyuan-yu-yan-mo-xing-n-gram">1.n元语言模型(n-gram)</h2>
<p>假设$S$表示长度为$i$,由$(w_1,w_2,\dots,w_m)$字序列组成的句子,则代表$S$的概率为:</p>
<p>$$
P(S) = P(w_1,w_2,\dots,w_m) = P(w_1)*P(w_2|w_1)*P(w_3|w_2,w_1)\cdots P(w_i|w_1,w_2,\dots,w_{m-1})
$$</p>
<ul>
<li>$n=1$,uni-gram</li>
</ul>
<p>$$
P(w_1,w_2,\dots,w_m) =\prod_{i=1}^mP(w_i)
$$</p>
<ul>
<li>$n=2$,bi-gram</li>
</ul>
<p>$$
P(w_1,w_2,\dots,w_m) =\prod_{i=1}^mP(w_i|w_{i-1})
$$</p>
<ul>
<li>$n=3$,tri-gram</li>
</ul>
<p>$$
P(w_1,w_2,\dots,w_m) =\prod_{i=1}^mP(w_i|w_{i-2}w_{i-1})
$$</p>
<h2 id="2-ji-yu-hmmde-fen-ci">2.基于HMM的分词</h2>
<p><strong>HMM的参数</strong></p>
<p>观测序列(输出状态序列),状态序列(隐藏状态序列),初始概率,转移概率(转移概率矩阵),发射概率(发射概率矩阵)
<img src="https://blog.vhcffh.com/2019/hexo-ji-yu-tong-ji-de-fen-ci-fang-fa/9_8_ma.png" alt="" /></p>
<p><strong>数学定义</strong></p>
<p>状态值集合(隐藏状态):$Q={q_1,q_2,\cdots,q_N}$,$N$为可能的状态数,对应状态序列$I$</p>
<p>观测值集合(输出状态):$V={v_1,v_2,\cdots,v_M}$,$M$为可能的观测数,对应观测序列$O$</p>
<p>转移概率矩阵:$A=[a*{ij}];i,j\in{1,2,\cdots,N}$,从$i$状态到$j$状态的转换概率($\sum*{j=1}^Na_{ij}=1$)</p>
<p>发射概率矩阵(观测概率矩阵):$B=[b_j(k)];j\in{1,2,\cdots,N},k\in{1,2,\cdots,M}$,从状态$j$生成观测$k$的概率</p>
<p>初始状态分布:$\pi$</p>
<p>模型:$\lambda=(A,B,\lambda)$,状态序列$I$,观测序列$O$</p>
<p><strong>HMM中的三个问题:</strong></p>
<ul>
<li>
<p>概率计算问题:已知模型,求观测序列$O$出现的概率;前向后向算法</p>
</li>
<li>
<p>学习问题:已知观测序列,求模型参数,最大化$P(O|\lambda)$;鲍姆-韦尔奇(Baum-Welch)算法</p>
<ul>
<li>
<p>已知隐藏序列和观测序列(通过频数估计)</p>
<p>$A_{ij}$表示隐藏状态$q_i$转移到$q_j$的频率计数</p>
<p>$$
A=[a_{ij}];a_{ij}=\frac{A_{ij}}{\sum_{s=1}^NA_{is}}
$$</p>
<p>$B_{jk}$表示隐藏状态$q_j$转移到观测状态$v_k$的频率计数</p>
<p>$$
B=[b_j(k)];b_j(k)=\frac{B_{jk}}{\sum_{s=1}^MB_{js}}
$$</p>
<p>$C(i)$为所有样本中初始隐藏状态$q_j$的频率计数
$$
\Pi = \pi(i)=\frac{C(i)}{\sum_{s=1}^NC(s)}
$$</p>
</li>
<li>
<p>仅知观测序列(鲍姆-韦尔奇算法,EM算法)</p>
<p>EM算法:最大似然估计用于没有隐变量的概率模型,EM算法可以用于有隐变量的算法模型</p>
<p>模型参数:</p>
<p>$$
\overline{\lambda} = arg;\max_{\lambda}\sum\limits_{I}P(I|O,\overline{\lambda})logP(O,I|\lambda)
$$ </p>
</li>
</ul>
</li>
<li>
<p>解码问题:已知模型$\lambda$与观测序列$O$,求状态序列$I$最大化$P(I|O)$;维特比(Viterbi)算法</p>
</li>
</ul>
<p>HMM是一种序列模型,不仅可以用到自然语言处理这个领域,其他领域的应用也很常见:<a href="http://tecdat.cn/%e7%94%a8%e6%9c%ba%e5%99%a8%e5%ad%a6%e4%b9%a0%e8%af%86%e5%88%ab%e4%b8%8d%e6%96%ad%e5%8f%98%e5%8c%96%e7%9a%84%e8%82%a1%e5%b8%82%e7%8a%b6%e5%86%b5-%e9%9a%90%e9%a9%ac%e5%b0%94%e7%a7%91%e5%a4%ab/">股指预测</a>,<a href="https://www.jianshu.com/p/16fc3712fdf6">语音识别</a>,<a href="http://www.c-a-m.org.cn/CN/abstract/abstract4637.shtml">网络安全</a>,<a href="https://arxiv.org/abs/1901.06286">基因序列</a>等方面</p>
<p>在自然语言处理中,HMM可以应用在分词,词性标注,命名实体识别等各个方面.</p>
<p>在分词方面可以这样理解HMM</p>
<p>观测序列(输出状态序列)----序列构成的句子或短文</p>
<p>状态序列(隐藏状态序列)----标注</p>
<p>初始概率----统计的第一个字序的概率</p>
<p>转移概率(转移概率矩阵)----第$i$个字序到第$i+1$个自序的标注变换概率</p>
<p>发射概率(发射概率矩阵)----从隐藏状态到输出状态的转换概率</p>
<p>如果把观测序列看作标注,状态序列看作句子,从解码问题转变成学习问题会怎样</p>
<p>在序列预测问题中</p>
<p>HMM模型:当前tag仅依赖前一个tag,当前输出仅依赖当前tag(文档的单词序列是由隐藏状态的标签决定的)</p>
<p>MEMM(最大熵马尔科夫模型)模型:当前tag取决于观察值x(单词)和前一个tag(序列的标签取决于前一个标签和当前的单词)</p>
<p>CRF模型:计算损失时把一句话看作一个整体计算损失</p>
<h2 id="can-kao-zi-liao">参考资料</h2>
<p>1.<a href="https://www.cnblogs.com/hiyoung/archive/2018/09/25/9703976.html">https://www.cnblogs.com/hiyoung/archive/2018/09/25/9703976.html</a></p>
<p>2.<a href="http://www.52nlp.cn/hmm-learn-best-practices-one-introduction">http://www.52nlp.cn/hmm-learn-best-practices-one-introduction</a></p>
<p>3.<a href="https://www.cnblogs.com/en-heng/p/6164145.html?utm_source=debugrun&utm_medium=referral">https://www.cnblogs.com/en-heng/p/6164145.html?utm_source=debugrun&utm_medium=referral</a></p>
<p>4.<a href="http://www.wanguanglu.com/2017/01/03/crf-introduction/">http://www.wanguanglu.com/2017/01/03/crf-introduction/</a></p>
神经图灵机
2019-09-07T00:00:00+00:00
2019-09-07T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-shen-jing-tu-ling-ji/
<h3 id="neural-turing-machinesyuan-wen">Neural Turing Machines<a href="https://arxiv.org/abs/1410.5401">原文</a></h3>
<ul>
<li>
<p>读记忆(Read Heads)</p>
<p>把时刻$t$的记忆看作是一个$ N \times M $的矩阵$ M_t $,读的过成首先生成长度为$N$的定位权重向量$w_t$,表示$N$个记忆位置的权值大小,读出的记忆向量为$r_t$:</p>
</li>
</ul>
<p>$$
r_t=\sum_i^Nw_t(i)M_t(i) \ \ \ 其中 \sum_iw_t(i)=1
$$</p>
<p>对$N$条记忆进行加权求和</p>
<ul>
<li>
<p>写记忆(Write Heads)</p>
<p>类似LSTM:擦除向量$e_t$,增加向量$a_t$</p>
<ul>
<li>擦除操作:</li>
</ul>
<p>$$
M_t'(i)=M_{t-1}(1-w_t(i)e_t(i))
$$</p>
<ul>
<li>增加操作:</li>
</ul>
<p>$$
M_t(i)=M_t'(i)+w_t(i)a_t(i)
$$</p>
<p>神经图灵机的关键是定位向量$w_t$,其它的是由控制器(LSTM,MLP)输出</p>
</li>
<li>
<p>定位机制(Addressing Mechanism)</p>
<p>结合了基于内容和基于位置的两种方法</p>
<ul>
<li>
<p>基于内容(Content-based Addressing)</p>
<p>$$
w_t^c(i)=\frac {\exp(\beta_tK[k_t,M_t(i)])}{\sum_j\exp(\beta_tK[k_t,M_t(j)])}
$$</p>
<p>$K[...]$是余弦相似度计算:</p>
<p>$$
K[u,v]=\frac{u \cdot v}{\Vert u\Vert \cdot \Vert v\Vert}
$$</p>
<p>$\beta_t$是控制器输出</p>
</li>
<li>
<p>基于位置(Location-based Addressing)</p>
<ul>
<li>
<p>插值(Interpolation)
$$
w_t^g=g_tw_t^c+(1-g_t)w_{t-1}
$$</p>
<p>$g_t$有控制器生成</p>
</li>
<li>
<p>偏移(shift)
$$
\tilde w_t(i)=\sum_{j=0}^{N-1}w_t^g(j)s_t(i-j)
$$</p>
<p>每一个$\tilde w_t(i)$都与相邻元素有关</p>
</li>
<li>
<p>重塑(Sharping)
$$
w_t(i)=\frac{\tilde w_t(i)^{\gamma_t}}{\sum_j\tilde w_t(j)^{\gamma_t}}
$$</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
小样本学习的边缘标签图神经网络
2019-09-06T00:00:00+00:00
2019-09-06T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-xiao-yang-ben-xue-xi-de-bian-yuan-biao-qian-tu-shen-jing-wang-luo/
<h3 id="edge-labeling-graph-neural-network-for-few-shot-learning-yuan-wen">Edge-Labeling Graph Neural Network for Few-shot Learning[<a href="https://arxiv.org/abs/1905.01436">原文</a>]</h3>
<p>小样本学习的边缘标签图神经网络</p>
<p>将样本映射到图中的节点,边表示节点之间的相似度,通过已知样本预测未知样本</p>
<h4 id="graph-neural-network">Graph Neural Network</h4>
<p>主要利用邻域聚合框架(通过对相邻节点特征的递归聚合和转换,计算出节点特征)进行表征学习</p>
<h4 id="edge-labeling-graph">Edge-Labeling Graph</h4>
<p><strong>Correlation clustering</strong></p>
<p>相关性聚类分析:一个通过同时最大化簇内相似性和簇间差异性来实现边标注推理的图分割算法</p>
<ul>
<li>通过结构化向量机实现名词短语聚类和新闻文章聚类</li>
</ul>
<h4 id="few-shot-learning">Few-Shot Learning</h4>
<p>C-way K-shot:一个meta-task,包括support
set随机抽取C个类别,每个类别K个样本(共CK个数据);和batch
set从这C各类别种抽取一批样本.</p>
<p>Mode Based, Metric Based,Optimization Based</p>
<h4 id="problem-definition-few-shot-classification">Problem definition: Few-shot classification</h4>
<ul>
<li>在每个类只有很少样本的情况下学习一个分类器</li>
<li>每一个few-shot分类任务$ \mathcal T $包括support set $ \mathcal S $和query set $ \mathcal Q $</li>
<li>episodic training:在training task中抽样,模拟少样本测试时的场景
<ul>
<li>$ \mathcal T = \mathcal S \cup \mathcal U $,
$ \mathcal S = {(x_i,y_i)}_{i=1} ^{N \times K} $
and $ \mathcal Q={(x_i,y_i)}_{i=N \times K + 1}^{N \times K+T} $</li>
<li>$x_i,y_i\in{C_1,...,C_N}=\mathcal C*{\mathcal
T}\subset\mathcal C$同时$\mathcal C*{train}\cap\mathcal
C*{test} = \empty$</li>
</ul>
</li>
</ul>
<h4 id="model">Model</h4>
<ol>
<li>通过卷积神经网络提取样本特征,$ \theta_{emb} $是卷积网络的参数</li>
</ol>
<p>$$
\mathbf v_i^0=f_{emb}(x_i;\theta_{emb})
$$</p>
<ol start="2">
<li>
<p>图构建:$ \mathcal g=(\mathcal V,\mathcal E;\mathcal T) $每一个节点代表一个样本,全连接图,每一条边代表一种关系种类</p>
<p>其中$ \mathcal V :={V_i}_{i=1,\dots,|\mathcal T|}$,$\mathcal
E :={E*{ij}}*{i,j=1,\dots,|\mathcal T|}$,</p>
<ol>
<li>
<p>ground truth: edge-label</p>
</li>
<li>
<p>边特征$\mathbf e*{ij}={e*{ijd}}^2_{d=1} \in
[0,1]^2$是一个二维矩阵,表示两个之节点间的簇内关系和簇间关系的强弱,||表示连接</p>
</li>
</ol>
</li>
<li>
<p>传播:设从$\ell -1 $层得到的特征为 $ \mathbf v*i^{\ell-1}$
和$\mathbf e*{ij}^{\ell-1}$</p>
<ol>
<li>
<p>更新节点特征</p>
<p>其中$ \tilde e_{ij1}^{\ell-1}=\frac {e_{ijd}}{\sum_ke_{ikd}}$,$f_v^\ell(\theta)$是节点特征的转变网络(MLP).</p>
</li>
<li>
<p>更新边特征</p>
<p>其中 $f_e^\ell$是metric network计算节点相似度</p>
</li>
</ol>
</li>
<li>
<p>输出结果:节点属于某个集合的分布$P(y_i=\mathcal C_k|\mathcal
T)=p_i^{(k)}$</p>
</li>
</ol>
<h4 id="training">Training</h4>
<p>其中模型参数有$ \theta*{emb}
\cup{\theta_v^\ell,\theta_e^\ell}_{\ell=1}^L $,对于M个训练任务${ \mathcal T_m^{train}}_{m=1}^M $,损失函数:</p>
<p>其中$Y*{m,e}$和$\hat
Y*{m,e}^\ell$是m个任务,e条边的真实值和预测值(第$\mathcal
L$层的),$\mathcal L$是交叉熵损失函数</p>
记一次GRUB引导修复
2019-09-03T00:00:00+00:00
2019-09-03T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-ji-yi-ci-grubyin-dao-xiu-fu/
<p>使用Arch Linux已经2年多了,基本上已经习惯了,可前段时间英雄联盟也出了"自走棋",想要体验一把。
于是就在硬盘剩余的60多个G上装了Windows 10,用了一两个月没有问题,最后在一次Windows 10更新后,蓝屏了。
又手惨的点了Windows 的修复功能。等了半天,结果是修复,重启,蓝屏无限循环。
而且Arch Linux的引导全都没了,感觉应该是Windows的修复动了efi分区。
无奈只能重新修复引导,各种方法尝试了好多遍,都是卡在了<code>GRUB _</code>一直闪这个状态,
最终在官网wiki找到了办法 <a href="https://wiki.archlinux.org/index.php/GRUB_#Default/fallback_boot_path">缺省/后备启动路径</a> grub 安装时添加<code>--removable</code>参数</p>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">grub-install --target=x86_64-efi --efi-directory=esp --removable
</code></pre>
<p>或者手动移动</p>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">mv esp/EFI/grub esp/EFI/BOOT
mv esp/EFI/BOOT/grubx64.efi esp/EFI/BOOT/BOOTX64.EFI
</code></pre>
<p>grub出现bug的原因应该会千奇百怪,要尽可能多的尝试网上的各种修改方法。</p>
深度学习在中文分词和词性标注中的应用
2019-08-25T00:00:00+00:00
2019-08-25T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-shen-du-xue-xi-zai-zhong-wen-fen-ci-he-ci-xing-biao-zhu-zhong-de-ying-yong/
<p>得到字向量->通过窗口方法得到字与上下文有关的向量(矩阵)->通过两个线性层和一个非线性激活函数->字的标注得分(窗口方法)->一个句子的评分矩阵$ f \times \theta (c \times {[1:n]}) $ (句子中的第$ i $ 个子为标签$ t $
的得分)->定义转换分数$ A_{ij} $,得到tag path 得分 ->最大化得分,得到最优tag path</p>
<p>$$
s(c_{[1:N]},t_{[1:N]},\theta)=\sum_{i=1}^n(A_{t_{i-1}t_i}+f_\theta (t_i\vert i))
$$</p>
<p>log likelihood</p>
<p>$$
\sum_{\forall(c,t)\in R}\log p(t\vert c,\theta)
$$</p>
<p>将目标函数转换为条件概率</p>
<p>$$
p(t\vert c,\theta)=\frac {e^{s(c,t,\theta)}}{\sum_{\tilde t}{e^{s(c,\tilde t,\theta)}}}
$$</p>
<p>取对数</p>
<p>$$
\log p(t\vert c,\theta) = s(c,t,\theta)-log\sum_{\tilde t}{e^{s(c,\tilde t,\theta)}}
$$</p>
<p>维特比算法(viterbi)</p>
<p>一种动态规划算法(穷举法,A*算法,beam search,Viterbi算法)</p>
<p><font color="red">A*算法和Viterbi算法的区别?</font></p>
<p>新的训练方法</p>
<p>将维特比算法在当前参数下得出的最优路径结果与正确结果进行比较对比,定义出损失函数对$ A_{t_{i-1}t_i} $
和$f_\theta (t_i|i) $ 的偏导数,通过后向传播更新参数</p>
<p>收敛性的证明:Discriminative training methods for hidden Markov models</p>
OWL基础
2019-08-22T00:00:00+00:00
2019-08-22T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-owlji-chu/
<p>OWL基础</p>
<p>网络本体语言Web Ontologoy Language</p>
<p>OWL Lite -> OWL DL -> OWL Full 递进关系</p>
<p>基本元素</p>
<ol>
<li>
<p>类(Class)</p>
<p>任何东西都是类owl:Thing
的一个成员(实例);子类:Subclass;相等关系:equivalentClass</p>
</li>
<li>
<p>个体(Individual)</p>
<p>与实例的概念差不多</p>
<p>一个个体可以属于多个类(没有指名是哪个类)</p>
<p>某个类的实例(指名了类)</p>
<p>子类与个体是不同的东西</p>
</li>
<li>
<p>属性(Property)</p>
<p>一个二元关系,OWL中包括两种属性:</p>
<ol>
<li>类型属性(datatype properties):描述类与其实例之间关系的属性。</li>
<li>对象属性(object
properties):描述两个不同类的实例之间关系的属性。</li>
</ol>
<p>属性有两个端点:起点和终点,都应该是两个个体(实例)</p>
<p>用原集(domain)起点的实例的类,用象集(range)描述终点的实例的类.</p>
<p>属性也有子属性(Subproperty)</p>
</li>
</ol>
<p>公理和约束</p>
<p>rdf资源描述框架(Resource Description Framework)</p>
<p>W3C提出的一组标记语言的技术规范,是一种数据模型,rdf数据集的序列化方法</p>
<p>OWL是由DAML(DARPA Agent Markup Language)+OIL(Ontology Inference
Layer)演变而来。</p>
<p>OWL是RDF的扩张,为我们提供了更广泛的定义RDFS词汇的功能,更广泛意指可以定义词汇之间的关系,类与类间的关系,属性与属性之间的关系等</p>
<p>foaf(Friend-of-a-Friend)是一种XML/RDF词汇表,不管通过那种那种方法表示数据资源,都要指定词汇表</p>
<ol>
<li>
<p>RDF/XML</p>
<p>XML的技术程序;格式太冗长,不便于阅读</p>
</li>
<li>
<p><a href="https://www.w3.org/TR/n-triples/">N-Triples</a></p>
<p>三元组表示;开放领域知识图谱<a href="https://wiki.dbpedia.org/">DBpedia</a>通常使用这种格式发布数据.</p>
</li>
<li>
<p><a href="https://www.w3.org/TR/turtle/">Turtle</a></p>
<p>使用最多的一种RDF序列化方法,比RDF/XML紧凑,可读性比N-Triples好</p>
</li>
<li>
<p>RDFa</p>
<p>The Resource Description Framework in
Attributes,HTML5的一个扩展,不改变任何显示效果的情况下,然网站更容易被搜索引擎解析</p>
</li>
<li>
<p>JSON-LD</p>
<p>JSON for Linking Data,用键值对的方法来存储RDF</p>
</li>
</ol>
<p>RDF/XML</p>
<pre><code><?xml version="1.0"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cd="http://www.recshop.fake/cd#">
<rdf:Description
rdf:about="http://www.recshop.fake/cd/Empire Burlesque">
<cd:artist>Bob Dylan</cd:artist>
<cd:country>USA</cd:country>
<cd:company>Columbia</cd:company>
<cd:price>10.90</cd:price>
<cd:year>1985</cd:year>
</rdf:Description>
<rdf:Description
rdf:about="http://www.recshop.fake/cd/Hide your heart">
<cd:artist>Bonnie Tyler</cd:artist>
<cd:country>UK</cd:country>
<cd:company>CBS Records</cd:company>
<cd:price>9.90</cd:price>
<cd:year>1988</cd:year>
</rdf:Description>
.
.
.
</rdf:RDF>
</code></pre>
<p>RDF 文档的第一行是 XML 声明。这个 XML 声明之后是 RDF 文档的根元素:。</p>
<p><em>xmlns:rdf</em> 命名空间,规定了带有前缀 rdf 的元素来自命名空间
"<a href="http://www.w3.org/1999/02/22-rdf-syntax-ns#%22%E3%80%82">http://www.w3.org/1999/02/22-rdf-syntax-ns#"。</a></p>
<p><em>xmlns:cd</em> 命名空间,规定了带有前缀 cd 的元素来自命名空间
"<a href="http://www.recshop.fake/cd#%22%E3%80%82">http://www.recshop.fake/cd#"。</a></p>
<p>元素包含了对被 <em>rdf:about</em> 属性标识的资源的描述。</p>
<p>元素:、、 等是此资源的属性。</p>
<p>Turtle序列化方法总结</p>
<ul>
<li>
<p>URI用 <strong><></strong> 描述</p>
<pre><code><http://example.org/path/>
<http://example.org/path/#fragment>
</code></pre>
</li>
<li>
<p>前缀缩写(类似于RDF/XML的命名空间)</p>
<pre><code>@prefix foo:<http://example.org/ns#>
@prefix : <http://example.org/ns1#>
:a :b :c
</code></pre>
</li>
<li>
<p>字面量</p>
<p>一行或者多行, <code>@en</code>限定其语言, <code>^^xsd:decimal</code>限定其数据类型</p>
<pre><code>"string"
"""
many lines of string
many lines of string
many lines of string
"""
"chat"@en
"chat"@fr
"10"^^xsd:decimal
</code></pre>
</li>
<li>
<p>空节点(RDF模型可能会存在未命名的空节点)</p>
<p><code>_:me</code>,<code>_a1234</code>分别代表一个空节点</p>
<pre><code>_:me
_:a1234
</code></pre>
</li>
<li>
<p>base URI</p>
<p>base URI定义后,接下来的<strong>URI, 前缀缩写,qualified names 和base
URI</strong>都要受其作用</p>
<pre><code># this is a complete turtle document
@base <http://example.org/ns/> .
# base URIs 是 http://example.org/ns/
@base <foo/> .
# base URI 是 http://example.org/ns/foo/
@prefix : <bar#> .
:a4 :b4 :c4.
</code></pre>
</li>
<li>
<p>对三元组进行缩写</p>
<pre><code>:a :b :c,
:d.
#the last triple is :a :b :d.
</code></pre>
</li>
<li>
<p>一个简单的完整turtle标准文件</p>
</li>
</ul>
<pre><code>@prefix info: <http://zy.example.com/info#>
@prefix rel: <http://zy.example.com/rel#>
@prefix person: <http://zy.example.com/person#>
person:Tom info:name "Tom";
info:job "worker";
info:age 56;
rel:fatherof person:Jim.
person:Jim info:name "Jim";
info:job "programmer";
info:age 28;
rel:fatherof person:Cherry.
person:Cherry info:name "Cherry";
info:age 8;
.
</code></pre>
sklearn中的广义线性模型
2019-08-20T00:00:00+00:00
2019-08-20T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-sklearnzhong-de-yan-yi-xian-xing-mo-xing/
<p>模型的通用公式</p>
<p>$$
\hat y(w,x)=w_0+w_1x_1+\dots+w_px_p
$$</p>
<p>普通最小二乘法</p>
<p>$$
w=\min_w{\Vert Xw-y\Vert_2}^2
$$</p>
<p>LinearRegression</p>
<p>岭回归</p>
<p>$$
w=\min_w{\Vert Xw-y\Vert_2}^2+\alpha {\Vert w \Vert_2}^2
$$</p>
<p>$ \alpha $ 是控制系数收缩量的复杂性参数: $ \alpha $ 的值越大,收缩量越大,模型对共线性的鲁棒性也更强。</p>
<p>共线性:线性回归模型中的解释变量之间由于存在精确相关关系或高度相关关系而使模型估计失真</p>
<p>Ridge,RigdeCV:广义交叉验证(GCV),默认留一验证(LOO-CV)</p>
<p>Lasso</p>
<p>$$
w = \min_w\frac{1}{2n_{samples}}\Vert Xw-y \Vert_2^2 + \alpha \Vert w \Vert_1
$$</p>
<p>$ \alpha $ 是常数,$ \Vert w \Vert \dot 1 $ 是参数向量的$ l \dot {1-norm} $范数</p>
<p>Lasso,lasso_path:通过搜索所有可能的路径上的值来计算系数</p>
<p>LassoCV,LassoLarsCV,LassoLarsIC</p>
<p>多任务Lasso</p>
<p>$$
w = \min_w\frac{1}{2n_{samples}}\Vert XW-Y\Vert_{Fro}^2+\alpha\Vert W \Vert_{21}
$$</p>
<p>$$
\Vert A \Vert_{Fro}=\sqrt{\sum_{ij}a_{ij}^2}
$$</p>
<p>$$
\Vert A \Vert_{21}= \sum_i\sqrt{\sum_j a_{ij}^2}
$$</p>
<p>MultiTaskLasso</p>
<p>弹性网络</p>
<p>$$
w = \min_w\frac{1}{2n_{samples}}\Vert Xw-Y\Vert_2^2+\alpha\rho\Vert w \Vert_{1}+\frac{\alpha(1-\rho)}{2}\Vert w\Vert_2^2
$$</p>
<p>ElasticNetCV通过交叉验证来设置参数<code>alpha</code>($ \alpha $)和<code>l1_rati0</code>($ \rho $)</p>
<p>多任务弹性网络</p>
<p>$$
W = \min_W\frac{1}{2n_{samples}}\Vert XW-Y\Vert_{Fro}^2+\alpha\rho\Vert W \Vert_{21}+\frac{\alpha(1-\rho)}{2}\Vert w\Vert_{Fro}^2
$$</p>
<p>MultiTaskElasticNet</p>
集成学习算法总结
2019-08-19T00:00:00+00:00
2019-08-19T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-ji-cheng-xue-xi-suan-fa-zong-jie/
<p>集成学习算法是指使用多个分类器提高整体的泛化能力</p>
<h1 id="1-bagging-bootstrap-aggregating-suan-fa">1.Bagging(Bootstrap Aggregating)算法</h1>
<p>通过组合随机生成的训练集而改进分类的集成算法(bootstrap)</p>
<p>使用训练集中的某个子集作为当前训练集(有放回随机抽样);经过T次训练后,得到T个不同的分类器</p>
<p>调用这T个分类器,把这T个分类结果中出现次数多的类赋予测试样例</p>
<p>有效减少噪声影响</p>
<h1 id="2-boostingsuan-fa">2.Boosting算法</h1>
<p>初始化样本权重,生成一个弱分类器;</p>
<p>利用弱分类器增加分类错误的样本的权重;</p>
<p>不断重复,生成T个弱分类器;</p>
<p>对噪声敏感</p>
<p>改进算法-AdaBoosting算法</p>
<ul>
<li>对每一次的训练数据样本赋予一个权重,并且每一次样本的权重分布依赖上一次的分类结果</li>
<li>基分类器之间采用序列的线性加权方式来组合</li>
</ul>
<h1 id="3-gradient-boosting">3.Gradient Boosting</h1>
<p>1,初始化</p>
<p>$$
f_0(x)=\arg\min_\gamma\sum_{i=1}^NL(y_i,\gamma)
$$</p>
<p>2.1计算负梯度</p>
<p>$$
\widetilde y_i = -\frac{\partial L(y_i,f_{m-1}(x_i))}{\partial f_{m-1}(x_i)}, i=1,2,\cdots N
$$</p>
<p>2.2用基学习器$ h_m(x) $ 拟合$ \widetilde y_i $</p>
<p>$$
w_m=\mathop{\arg\min}\limits_w\sum_{i=1}^N[\widetilde y_i-h_m(x_i;w)]^2
$$</p>
<p>2.3确定步长$ \rho_m $</p>
<p>$$
\rho_m = \mathop{\arg\min}\limits_{\rho} \sum\limits_{i=1}^{N} L(y_i,f_{m-1}(x_i) + \rho h_m(x_i,;,w_m))
$$</p>
<p>2.4更新$ f_m(x) $ 最终得到$ f_M(x) $</p>
<p>$$
f_m(x) = f_{m-1}(x) + \rho_m h_m(x,;,w_m)
$$</p>
<p>Bagging + 决策树 = 随机森林</p>
<p>AdaBoost + 决策树 = 提升树</p>
<p>Gradient Boosting + 决策树 = GBDT</p>
信息论的一些基本概念
2019-08-18T00:00:00+00:00
2019-08-18T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-xin-xi-lun-de-yi-xie-ji-ben-gai-nian/
<p><strong>信息熵</strong></p>
<p>$$
H(X)=E[I(X)]=E[-ln(P(X))]
$$</p>
<p>其中$ P $ 为$ X $的概率质量函数,$ E $为期望函数,而$ I(x) $是$ X $的信息量(又称自信息).</p>
<p>$$
H(X)=\sum_iP(x_i)I(x_i)=-\sum_iP(x_i)\log_bP(x_i)
$$</p>
<p>$$
\begin{matrix}
b & 熵的单位\cr
2 & bit\cr
e & nat\cr
10 & Hart
\end{matrix}
$$</p>
<p><strong>条件熵(Conditional Entropy)</strong></p>
<p>特征$ x $ 固定为$ x_i $时:$ H(c|x_i) $</p>
<p>特征$ x$ 整体分布已知时:$ H(x|X) $</p>
<p><strong>信息增益(Information Gain)</strong></p>
<p>$$
IG(X) = H(c)-H(c|X)
$$</p>
<p><strong>基尼系数(基尼不纯度Gini impurity)</strong></p>
<p>$$
Gini(D)=1-\sum_i^np_i^2
$$</p>
<p>$$
Gini(D|A)=\sum_i^n\frac {D_i}{D}
$$</p>
<p><strong>信息增益比率(Information Gain Ratio)与分裂信息(Split information)</strong></p>
<p>$$
GR(D|A)=\frac {IG(D|A)}{SI(D|A)}
$$</p>
<p>$$
SI(D|A)=-\sum_i^n\frac {N_i}{N}\log_2\frac{N_i}{N}
$$</p>
<p><strong>边界熵(boundary entropy)</strong></p>
<p>$$
BE(w_1w_2\cdots w_k) = -\sum_{w \in C}p(w\vert w_1w_2\cdots w_k)\log p(w\vert w_1w_2\cdots w_k)
$$</p>
<p>$ w $是邻接于$ w_1w_2 \cdots w_k $ 的字符.</p>
<p><strong>边界多样性(Accessor veriety,AV)</strong></p>
<p>$$
AV(w_1w_2\cdots w_k)=\log RL_{av}(w_1w_2\cdots w_k)
$$</p>
<p>$ RL_{av} $ 表示邻接于字符串$ w_1w_2 \cdots w_k $的不同字符个数.</p>
mathjax配置问题
2019-08-10T00:00:00+00:00
2019-08-10T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-mathjaxpei-zhi-wen-ti/
<p>使用hexo时,想要实现网页中公式的渲染<br />
发现不管怎么改,都不能渲染单行公式<br />
最后发现是在mathjax的2.3版本以后,配置方法变了</p>
<p>mathjax的配置方法</p>
<p>一般网上大部分的mathjax的配置如下</p>
<pre data-lang="html" class="language-html "><code class="language-html" data-lang="html"> <script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
processEscapes: true
}
});
</script>
<script type="text/javascript" src="path-to-MathJax/MathJax.js?config=TeX-AMS_HTML">
</script>
</code></pre>
<p>其中配置中这一句主要是增加对单行公式的渲染</p>
<pre><code>inlineMath: [ ['$','$'], ["\\(","\\)"] ],
</code></pre>
<p>有时候会发现无论如修改单行公式总是不能渲染<br />
原因是在mathjax的2.3版本以后,应该这样配置</p>
<pre data-lang="html" class="language-html "><code class="language-html" data-lang="html"> <script type="text/javascript">
window.MathJax = {
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
processEscapes: true
}
};
</script>
<script type="text/javascript" src="path-to-MathJax/MathJax.js?config=TeX-AMS_HTML">
</script>
</code></pre>
<p>对于<code>hexo</code>默认转义规则使单行公式显示错误的问题,查看<a href="https://ranmaosong.github.io/2017/11/29/hexo-support-mathjax/">这篇博客</a></p>
<p>参考资料</p>
<p>1,<a href="https://docs.mathjax.org/en/latest/configuration.html">https://docs.mathjax.org/en/latest/configuration.html</a></p>
pytorch使用和损失函数
2019-07-26T00:00:00+00:00
2019-07-26T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-pytorchshi-yong-he-sun-shi-han-shu/
<h1 id="ji-li-han-shu">激励函数</h1>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">#激活函数
x=np.arange(-12.5,12.5,0.05)
tanh = (np.power(np.e,x)-np.power(np.e,-x))/(np.power(np.e,x)+np.power(np.e,-x))
relu = np.maximum(0.0,x)
sigmoid = 1.0/(1.0+np.power(np.e,-x))
torch.nn.Sigmoid()
torch.nn.Tanh()
</code></pre>
<p>BatchNorm2d</p>
<p>对每一个特征进行正则</p>
<p>pytorch中的正则化函数</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.functional.normalize(input, p=2, dim=1, eps=1e-12, out=None)
torch.norm(input, p='fro', dim=None, keepdim=False, out=None, dtype=None)
# 对维度dim求p范数
torch.Tensor.squeeze()->Tensor#维度压缩
torch.cat(tensors, dim=0, out=None)->Tensor #维度拼接
torch.stack(tensors,dim=0,out=None)->Tensor #张量拼接
# cat是把多张纸拼成一张纸,stack是把纸摞起来
torch.Tensor.repeat()->Tensor #矩阵扩展
torch.Tensor.transpose()->Tensor #矩阵转置
torch.eq() #张量比较
torch.chunk() #张量分块
torch.split(tensor, split_size_or_sections, dim=0) #张量分块
</code></pre>
<h1 id="sun-shi-han-shu">损失函数</h1>
<p>L1Loss</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>MSELoss</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>CrossEntropyLoss</p>
<p>交叉熵损失函数,多分类</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
torch.nn.CTCLoss(blank=0, reduction='mean', zero_infinity=False)
</code></pre>
<p>NLLLoss</p>
<p>负对数似然损失函数(Negative Loss Likelihood),多分类</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.NLLLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
</code></pre>
<p>PoissonNLLLoss</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.PoissonNLLLoss(log_input=True, full=False, size_average=None, eps=1e-08, reduce=None, reduction='mean')
</code></pre>
<p>KLDivLoss</p>
<p>KL散度,又叫相对熵,计算两个分布之间的距离,越相近越接近零</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.KLDivLoss(size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>BCELoss</p>
<p>二分类用的交叉熵</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>BCEWithLogitsLoss</p>
<p>增加了一个Sigmoid层</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.BCEWithLogitsLoss(weight=None, size_average=None, reduce=None, reduction='mean', pos_weight=None)
</code></pre>
<p>MarginRankingLoss</p>
<p>评价相似度的损失</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.MarginRankingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>HingeEmbeddingLoss</p>
<p>用于学习非线性嵌入或半监督学习</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.HingeEmbeddingLoss(margin=1.0, size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>MultiLabelMarginLoss</p>
<p>多类别多分类的Hinge损失</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.MultiLabelMarginLoss(size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>其中$ x \in \lbrace 0, ; \cdots , ; \text{x.size}(0) - 1 \rbrace $,
$ y \in \lbrace 0, ; \cdots , ; \text{y.size}(0) - 1 \rbrace $,
$ 0 \leq y[j] \leq \text{x.size}(0)-1 $,$i \neq y[j] $</p>
<p>SmoothL1Loss</p>
<p>也叫Huber Loss,误差在(-1,1)上是平方损失,其他情况是L1损失</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.SmoothL1Loss(size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>SoftMarginLoss</p>
<p>多标签二分类问题</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.SoftMarginLoss(size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>MultiLabelSoftMarginLoss</p>
<p>多标签多分类</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.MultiLabelSoftMarginLoss(weight=None, size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>其中$ i \in \lbrace 0, ; \cdots , ; \text{x.nElement}() - 1\rbrace $,
$ y[i] \in \lbrace 0, ; 1 \rbrace $</p>
<p>CosineEmbeddingLoss</p>
<p>余玄相似度损失</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.CosineEmbeddingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>MultiMarginLoss</p>
<p>多分类的Hinge损失</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.MultiMarginLoss(p=1, margin=1.0, weight=None, size_average=None, reduce=None, reduction='mean')
</code></pre>
<p>其中$x \in \lbrace 0, ; \cdots , ; \text{x.size}(0) - 1 \rbrace $,$ i \neq y $</p>
<p>TripletMarginLoss</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python">torch.nn.TripletMarginLoss(margin=1.0, p=2.0, eps=1e-06, swap=False, size_average=None, reduce=None, reduction='mean')
</code></pre>
常见损失函数
2019-07-21T00:00:00+00:00
2019-07-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-chang-jian-sun-shi-han-shu/
<p>1.损失函数</p>
<blockquote>
<p><strong>损失函数</strong>是指一种将一个事件(在一个<a href="https://wiki.mbalib.com/wiki/%E6%A0%B7%E6%9C%AC">样本</a>空间中的一个元素)映射到一个表达与其事件<a href="https://wiki.mbalib.com/wiki/%E7%9B%B8%E5%85%B3">相关</a>的<a href="https://wiki.mbalib.com/wiki/%E7%BB%8F%E6%B5%8E%E6%88%90%E6%9C%AC">经济成本</a>或<a href="https://wiki.mbalib.com/wiki/%E6%9C%BA%E4%BC%9A%E6%88%90%E6%9C%AC">机会成本</a>的实数上的一种函数,较常运用在<a href="https://wiki.mbalib.com/wiki/%E7%BB%9F%E8%AE%A1%E5%AD%A6">统计学</a>,<a href="https://wiki.mbalib.com/wiki/%E7%BB%9F%E8%AE%A1%E5%86%B3%E7%AD%96%E7%90%86%E8%AE%BA">统计决策理论</a>和<a href="https://wiki.mbalib.com/wiki/%E7%BB%8F%E6%B5%8E%E5%AD%A6">经济学</a>中。损失函数参数的真值为(θ),<a href="https://wiki.mbalib.com/wiki/%E5%86%B3%E7%AD%96">决策</a>的结果为<em>d</em>
,两者的不一致会带来一定的损失,这种损失是一个<a href="https://wiki.mbalib.com/wiki/%E9%9A%8F%E6%9C%BA%E5%8F%98%E9%87%8F">随机变量</a>,用<em>L</em>(θ,<em>d</em>)表示。</p>
</blockquote>
<p>1.1 0-1损失函数(0-1 loss function)</p>
<p>$$
L(Y,f(X))=
\begin{cases}
1,Y \neq f(X)\cr
0,Y = f(X)
\end{cases}
$$</p>
<p>1.2平方损失函数(quadratic loss function)</p>
<p>$$
L(Y,f(X))=(Y-f(X))^2
$$</p>
<p>1.3绝对损失函数(absolute loss function)</p>
<p>$$
L(Y,f(X))=|Y-f(X)|
$$</p>
<p>1.4对数损失函数(logarithmic loss function)或对数似然损失函数(log likelihood loss function)</p>
<p>$$
L(Y,P(Y|X))=-logP(Y|X)
$$</p>
<p>2.风险函数
<strong>风险函数</strong>是损失函数的期望值,表示为:</p>
<p>$$
R(\theta,d)=E[L(d,\theta)]
$$</p>
<p>决策的目标是要找出一个决策方案$d$,使其对各个自然状态风险值均为最小。应用时,常常对θ(参数的真值)确定一个概率分布,并使其平均的风险值$r(d,\theta)$达到最小,其中:
$$
r(d,\theta) = E[R(d,\theta)]=\sum_{j=1}^LR(d,\theta)p(\theta_j)
$$
有结构风险函数和经验风险函数</p>
<p>参考资料</p>
<p>1,<a href="http://www.csuldw.com/2016/03/26/2016-03-26-loss-function/">http://www.csuldw.com/2016/03/26/2016-03-26-loss-function/</a></p>
神经网络中的前向传播与后向传播
2019-07-10T00:00:00+00:00
2019-07-10T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-shen-jing-wang-luo-zhong-de-qian-xiang-chuan-bo-yu-hou-xiang-chuan-bo/
<p><img src="https://blog.vhcffh.com/2019/hexo-shen-jing-wang-luo-zhong-de-qian-xiang-chuan-bo-yu-hou-xiang-chuan-bo/7_10_neu.png" alt="img" /></p>
<p>$ f(z) $为激励函数,关于激励函数(又称激活函数)的总结<br />
隐藏层1输入</p>
<p>$$
z^{(1)}=W^{(1)}x^T+b^{(1)}\tag{1}
$$</p>
<p>隐藏层1输出</p>
<p>$$
n^{(1)}=f^{(1)}(z^{(1)})\tag{2}
$$</p>
<p>隐藏层2输入</p>
<p>$$
z^{(2)}=W^{(2)}n^{(1)}+b^{(2)}\tag{3}
$$</p>
<p>隐藏层2输出</p>
<p>$$
n^{(2)}=f^{(2)}(z^{(2)})\tag{4}
$$</p>
<p>隐藏层3输入</p>
<p>$$
z^{(3)}=W^{(3)}n^{(2)}+b^{(3)}\tag{5}
$$</p>
<p>隐藏层3输出即输出层</p>
<p>$$
\widehat y = n^{(3)}= f^{(3)}(z{(3)})\tag{6}
$$</p>
<p>损失函数</p>
<p>$$
L(y,\widehat y)\tag{7}
$$</p>
<p>即隐藏层k+1输入</p>
<p>$$
z^{(k+1)}=W^{(k+1)}n^{(k)}+b^{(k+1)}\tag{8}
$$</p>
<p>隐藏层k+1输出</p>
<p>$$
n^{(k+1)}= f^{(k+1)}(z{(k+1)})\tag{9}
$$</p>
<p>对损失函数进行总结<a href="https://blog.csdn.net/lien0906/article/details/78429768">https://blog.csdn.net/lien0906/article/details/78429768</a><br />
计算偏导数</p>
<p>$$
\frac {\partial z^{(k)}}{\partial b^{(k)}}=
diag(1,1, \ldots ,1)\tag{10}
$$</p>
<p>列向量对列向量求导参见矩阵中的求导</p>
<p>计算偏导数$\frac {\partial L(y,\widehat y)}{\partial z^{(k)}}$</p>
<p>偏导数$ \frac {\partial L(y,\widehat y)}{\partial z^{(k)}}\ $
又称误差项(error term,也称"灵敏度"),一般用$ \delta $
表示,用$ \delta^{(k)} $
表示第k层神经元的误差项,其值的大小代表了<strong>第k层神经元对最终总误差的影响大小</strong></p>
<p>$$
\begin{align}
\delta^{(k)} & = \frac {\partial L(y,\widehat y)}{\partial z^{(k)}}\cr
& =\frac {\partial n^{(k)}}{\partial z^{(k)}}*
\frac {\partial z^{(k+1)}}{\partial n^{(k)}}*
\frac {\partial L(y,\widehat y)}{\partial z^{(k+1)}}\cr
& = {f^{(k)}}^{'}(z^{(k)}) * (W^{(k+1)})^T * \delta^{(k+1)}
\end{align}\tag{11}
$$</p>
<p>最终需要用的两个导数</p>
<p>$$
\frac {\partial L(y,\widehat y)}{\partial W^{(k)}}
=\frac {\partial L(y,\widehat y)}{\partial z^{(k)}}*
\frac {\partial z^{(k)}}{\partial W^{(k)}}
=\delta^{(k)}*(n^{(k-1)})^T\tag{12}
$$</p>
<p>$$
\frac {\partial L(y,\widehat y)}{\partial b^{(k)}}
=\frac {\partial L(y,\widehat y)}{\partial z^{(k)}}*
\frac {\partial z^{(k)}}{\partial b^{(k)}}
=\delta^{(k)}\tag{13}
$$</p>
<p>后向传播参数更新</p>
<p>$$
W^{(k)} = W^{(k)} - \alpha(\delta^{(k)}(n^{(k-1)})^T + W^{(k)})\tag{14}
$$</p>
<p>$$
b^{(k)} = b^{(k)}-\alpha\delta^{(k)}\tag{15}
$$
其中$ \alpha $ 是学习率</p>
<p>后向传播中的正则化,L1正则化,L2正则化</p>
深度学习中Keras vs Pytorch
2019-07-09T00:00:00+00:00
2019-07-09T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-shen-du-xue-xi-zhong-keras-vs-pytorch/
<p>深度学习框架Keras与Pytorch的区别与优劣<a href="https://towardsdatascience.com/keras-vs-pytorch-for-deep-learning-a013cb63870d">翻译</a><br />
<img src="https://blog.vhcffh.com/2019/hexo-shen-du-xue-xi-zhong-keras-vs-pytorch/7_11_pytorchvskears.png" alt="img" title="Keras vs PyTorch" /></p>
<p>对于许多科学家,工程师和开发人员来说,TensorFlow是他们的第一个深度学习框架。
TensorFlow 1.0于2017年2月发布;但它对用户来说不是很友好。</p>
<p>在过去几年中,两个主要的深度学习库已经获得了巨大的普及,主要是因为它们比TensorFlow更容易使用:<strong>Keras</strong>和<strong>Pytorch</strong>。</p>
<p>本文将介绍Keras与Pytorch的4个不同点,以及为什么您可以选择一个库而不是另一个库。</p>
<h1 id="keras">Keras</h1>
<hr />
<p>Keras本身不是一个框架,但实际上是一个位于其他Deep
Learning框架之上的高级API。目前它支持<a href="https://www.tensorflow.org/">TensorFlow</a>,<a href="http://deeplearning.net/software/theano/">Theano</a>和<a href="https://github.com/microsoft/CNTK">CNTK</a>。</p>
<p>Keras的优势在于它的易用性。它是迄今为止最容易去快速启动和运行的框架。定义神经网络非常直观,使用功能API允许人们将层定义为函数。</p>
<h1 id="pytorch">Pytorch</h1>
<hr />
<p>Pytorch是由Facebook的AI研究小组开发的深度学习框架(如TensorFlow)。像Keras一样,它也对深度网络编程中比较复杂的一大部分进行了抽象。</p>
<p>在高级和低级编码风格方面,Pytorch位于Keras和TensorFlow之间。你比Keras有更多的灵活性和控制力,但与此同时你不必做任何疯狂的声明性编程。</p>
<p>深度学习练习者整天都在争论应该使用哪个框架。一般来说,这取决于个人喜好。但是在选择时你应该记住Keras和Pytorch的一些特性。</p>
<p><img src="https://blog.vhcffh.com/2019/hexo-shen-du-xue-xi-zhong-keras-vs-pytorch/7_10_keras.jpeg" alt="img" title="It's keras" /></p>
<p><img src="https://blog.vhcffh.com/2019/hexo-shen-du-xue-xi-zhong-keras-vs-pytorch/7_10_pytorch.jpeg" alt="img" title="It's Pytorch!" /></p>
<h1 id="1-yong-yu-ding-yi-mo-xing-de-lei-pytorch-vs-han-shu-keras">(1)用于定义模型的 类(Pytorch) vs 函数(Keras)</h1>
<hr />
<p>要定义深度学习模型,Keras提供Functional API。 使用Functional
API,神经网络被定义为一组顺序函数,一个接一个地应用。
例如,第一层的输出是第二层的输入。</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> img_input = layers.Input(shape=input_shape)
x = layers.Conv2D(64, (3, 3), activation='relu')(img_input)
x = layers.Conv2D(64, (3, 3), activation='relu')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2))(x)
</code></pre>
<p>在Pytorch中,您将网络设置为一个类,该类继承了Torch库中的torch.nn.Module。
与Keras类似,Pytorch为您提供了层作为构建块,但由于它们位于Python类中,因此它们在类的__init__()方法中引用,并由类的forward()方法执行。</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 64, 3)
self.conv2 = nn.Conv2d(64, 64, 3)
self.pool = nn.MaxPool2d(2, 2)
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.pool(F.relu(self.conv2(x)))
return x
model = Net()
</code></pre>
<p>因为Pytorch允许您访问所有Python的类功能而不是简单的函数调用,所以定义网络可以更清晰,更优雅地包含。
这真的没什么不好的,除非你觉得最重要的是尽可能快地编写代码,那么Keras会更容易使用。</p>
<h1 id="2-zhang-liang-he-ji-suan-tu-pytorch-vs-biao-zhun-zhen-lie-keras">(2)张量和计算图(Pytorch) vs 标准阵列(Keras)</h1>
<p>Keras
API对程序员隐藏了的许多复杂细节。定义网络层非常直观,默认设置通常足以让您入门。</p>
<p>只有当你需要实现一个相当尖端或"异国情调"的模型时,你才真正需要去了解底层的TensorFlow。</p>
<p>棘手的部分是,当你真正去了解底层的TensorFlow代码时,你将获得随之而来的所有非常具有挑战性的部分!
您需要确保所有矩阵乘法都排成一行。
哦,甚至不要去考虑尝试打印出图层的一个输出,因为您只能在终端上打印出一个漂亮的Tensor定义。</p>
<p>Pytorch在这些方面倾向于更加方便。
您需要知道每个层的输入和输出大小,但这可以很快掌握。
您不必处理构建一个您无法在调试中看到的抽象计算图。</p>
<p>Pytorch的另一个好处是你可以在Torch Tensors和Numpy阵列之间来回滑动。
如果你需要实现自定义的东西,那么在TF张量和Numpy阵列之间来回转换可能会很麻烦,需要开发人员对TensorFlow的Session有充分的了解。</p>
<p>Pytorch的交互性比想象中要简单得多。 您只需要知道两个操作:一个将Torch
Tensor(一个Variable对象)转变到Numpy,另一个是相反的转换。</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> a = torch.Tensor(2,2)
print(a)
b = a.numpy() #tensor 变为numpy
print(b)
print(torch.from_numpy(b)) # numpy 变为tensor
</code></pre>
<p>当然,如果你不需要实现任何花哨的东西,那么Keras会做得很好,因为你不会遇到任何TensorFlow障碍。
但如果你这样做,那么Pytorch可能会更顺畅。</p>
<h1 id="3-training-models">(3) Training models</h1>
<hr />
<p>在Keras训练模型非常容易!
只是一个简单的.fit()函数,你可以很轻松的进行训练。</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> history = model.fit_generator(
generator=train_generator,
epochs=10,
validation_data=validation_generator)
</code></pre>
<p>在Pytorch中训练一些模型需要一些步骤</p>
<ul>
<li>每一批次的训练开始时初始化梯度</li>
<li>在模型中运行前向传播</li>
<li>运行后向传播</li>
<li>计算损失和更新权重</li>
</ul>
<p>所以,就训练模型来说,PyTorch 较为繁琐。</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> for epoch in range(2): # loop over the dataset multiple times
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# Get the inputs; data is a list of [inputs, labels]
inputs, labels = data
# (1) Initialise gradients
optimizer.zero_grad()
# (2) Forward pass
outputs = net(inputs)
loss = criterion(outputs, labels)
# (3) Backward
loss.backward()
# (4) Compute the loss and update the weights
optimizer.step()
</code></pre>
<h1 id="4-kong-zhi-cpu-vs-gpu-mo-shi">(4)控制 CPU vs GPU 模式</h1>
<p>如果你已经安装了 tensorflow-gpu,则在 Keras 中能够使用 GPU
并且会默认完成。然后,如果你想要将某些运算转移至
CPU,则可以以单行方式完成。</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> with tf.device('/cpu:0'):
y = apply_non_max_suppression(x)
</code></pre>
<p>但对于 PyTorch 来说,你必须显式地为每个 torch 张量和 numpy 变量启动
GPU。这样代码会比较混乱。并且如果你想在 CPU 和 GPU
之间来回移动以执行不同运算,则很容易出错。</p>
<p>例如,为了将之前的模型转移到 GPU 上运行,则需要以下步骤:</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> # Get the GPU device
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# Transfer the network to GPU
net.to(device)
# Transfer the inputs and labels to GPU
inputs, labels = data[0].to(device), data[1].to(device)
</code></pre>
<p>因而,Keras 在简洁性和默认设置方面优于 PyTorch。</p>
<h1 id="xuan-ze-keras-huo-pytorch-de-yi-ban-xing-jian-yi">选择 Keras 或 PyTorch 的一般性建议</h1>
<hr />
<p>作者通常建议初学者从 Keras 开始。Keras
绝对是理解和使用起来最简单的框架,能够很快地上手运行。你完全不需要担心
GPU 设置、处理抽象代码以及其他任何复杂的事情。你甚至可以在不接触任何
TensorFlow 单行代码的情况下,实现自定义层和损失函数。</p>
<p>但如果你开始深度了解到深度网络的更细粒度层面或者正在实现一些非标准的事情,则
PyTorch 是你的首选库。使用 PyTorch
需要进行一些额外操作,但这不会减缓你的进程。你依然能够快速实现、训练和测试网络,并享受简单调试带来的额外益处。</p>
优化算法总结
2019-07-04T00:00:00+00:00
2019-07-04T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-you-hua-suan-fa-zong-jie/
<h3 id="adagrad-adaptive-gradient-zi-gua-ying-ti-du">AdaGrad-Adaptive-Gradient-自适应梯度</h3>
<p>$$
g_t = \nabla_\theta J(\theta_{t-1})
$$</p>
<p>$$
\theta_{t+1} = \theta_t-\alpha\cdot g_t/\sqrt{\sum_{i=1}^tg_t^2} \ \ \ \ \ (\alpha=0.01)
$$</p>
<p>随梯度的变化改变学习率</p>
<h3 id="rmsprop">RMSProp</h3>
<p>$$
g_t = \nabla_\theta J(\theta_{t-1})
$$</p>
<p>$$
v_t= \gamma(v_{t-1})+(1-\gamma)g_t^2 \ \ \ \ \ (\gamma=0.9)
$$</p>
<p>$$
\theta_{t} = \theta_{t-1}-\alpha* g_t/(\sqrt v_t + \varepsilon) \ \ \ \ \ (\alpha=0.001,\varepsilon=10^{-8})
$$</p>
<p>结合梯度平方的指数移动平均数来调节学习率的变化</p>
<p>克服AdaGrad梯度急剧减小的问题</p>
<h3 id="adamyou-hua-qi">Adam优化器</h3>
<p>$$
g_t = \nabla_\theta J(\theta_{t-1})
$$</p>
<p>$$
m_t = \beta_1m_{t-1}+(1-\beta_1)g_t \ \ \ \ \ (\beta_1=0.9,m_0=0)
$$</p>
<p>$$
v_t=\beta_2v_{t-1}+(1-\beta_2)g_t^2 \ \ \ \ \ (\beta_2=0.999,v_0=0)
$$</p>
<p>$$
\hat m_t = m_t/(1-\beta_1^t)
$$</p>
<p>$$
\hat v_t = v_t/(1-\beta_2^t)
$$</p>
<p>$$
\theta_t = \theta_{t-1}-\alpha * \hat m_t/(\hat v_t + \varepsilon) \ \ \ \ \ (\alpha=0.001,\varepsilon=10^{-8})
$$</p>
<p>对梯度的一阶矩估计(First Moment Estimation,即梯度的均值)和二阶矩估计(Second Moment Estimation,即梯度的未中心化的方差)进行综合考虑,计算出更新步长。</p>
<h3 id="xia-mian-shi-dui-ji-chong-ti-du-xia-jiang-suan-fa-de-mo-ni">下面是对几种梯度下降算法的模拟</h3>
<p>其中包括随机梯度下降算法SGD,小批量梯度下降算法MSGD,批量梯度下降算法BGD</p>
<p>都是根据损失函数计算参数梯度,更新参数;不一样的地方是他们使用使用训练集的方式</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"># 随机梯度下降算法SGD
x=np.random.rand(10000,5)
w=np.array([[1,2,3,4,5],[20,30,40,50,60]])
b=np.array([[10],[100]])
y=(w.dot(x.T)+b).T #100,2
lr=0.5
ww = np.random.rand(2,5)
bb = np.random.rand(2,1)
mb=master_bar(range(1000000))
mb.names=['loss']
lossy=[]
for i in mb:
r=np.random.randint(1000)
xi=x[r:r+1,:] #1,5
yi=y[r:r+1,:] #1,2
yy = ww.dot(xi.T)+bb #2,1
loss = np.mean(np.power(yy-yi.T,2)/2)
lossy.append(loss)
graphs = [[np.arange(len(lossy)),lossy]]
mb.update_graph(graphs)
ww= ww - lr*(yy-yi.T).dot(xi)
bb= bb - lr*(yy-yi.T)
if loss < 1e-10:
break
#print(loss)
print(ww,bb)
print(w,b)
# 小批量梯度下降算法MSGD
x=np.random.rand(1000,5)
w=np.array([[1,2,3,4,5],[20,30,40,50,60]])
b=np.array([[10],[100]])
y=(w.dot(x.T)+b).T #100,2
lr=0.3
ww = np.random.rand(2,5)
bb = np.random.rand(2,1)
mb=master_bar(range(200*3))
mb.names=['loss']
lossy=[]
batch = 50
for i in mb:
lossg,wwg,bbg=0,0,0
for j in progress_bar(range(batch),parent=mb):
r=np.random.randint(1000)
xi=x[r:r+1,:] #1,5
yi=y[r:r+1,:] #1,2
yy = ww.dot(xi.T)+bb #2,10
lossg += np.mean(np.power(yy-yi.T,2)/2)
wwg += (yy-yi.T).dot(xi)
bbg += (yy-yi.T)
lossy.append(lossg/batch)
graphs = [[np.arange(len(lossy)),lossy]]
mb.update_graph(graphs)
ww= ww - lr*wwg/batch
bb= bb - lr*bbg/batch
if lossg/batch < 1e-4:
break
#print(loss)
print(ww,bb)
print(w,b)
# 批量梯度下降算法BGD
x=np.random.rand(1000,5)
w=np.array([[1,2,3,4,5],[20,30,40,50,60]])
b=np.array([[10],[100]])
y=(w.dot(x.T)+b).T #100,2
lr=0.3
#ww = np.random.rand(2,5)
#bb = np.random.rand(2,1)
mb=master_bar(range(200*3))
mb.names=['loss']
lossy=[]
batch = 1000
for i in mb:
lossg,wwg,bbg=0,0,0
for j in progress_bar(range(batch),parent=mb):
r=j
xi=x[r:r+1,:] #1,5
yi=y[r:r+1,:] #1,2
yy = ww.dot(xi.T)+bb #2,10
lossg += np.mean(np.power(yy-yi.T,2)/2)
wwg += (yy-yi.T).dot(xi)
bbg += (yy-yi.T)
lossy.append(lossg/batch)
graphs = [[np.arange(len(lossy)),lossy]]
mb.update_graph(graphs)
ww= ww - lr*wwg/batch
bb= bb - lr*bbg/batch
if lossg/batch < 1e-4:
break
#print(loss)
print(ww,bb)
print(w,b)
</code></pre>
Python中遇到的问题
2019-05-31T00:00:00+00:00
2019-05-31T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-pythonzhong-yu-dao-de-wen-ti/
<p>1.列表的初始化</p>
<p>当初始化一个n×n的列表时不能使用如下方法,</p>
<pre><code>In [1]: l=[[0]*3]*3 #如此初始化会导致其它行仅是第一行的引用而不是copy
In [2]: l
Out[2]: [[0, 0, 0], [0, 0, 0], [0, 0, 0]
In [3]: l[0][0]=1 #改变其中一行的某个元素
In [4]: l
Out[4]: [[1, 0, 0], [1, 0, 0], [1, 0, 0]] #其他行跟着改变
</code></pre>
<p>正确的方法应该如下</p>
<pre><code>In [5]: l=[[0 for _ in range(3)] for _ in range(3)] #或者l=[[0]*3 for _ in range(3)]
In [6]: l
Out[6]: [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
In [7]: l[0][0]=1
In [8]: l
Out[8]: [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
</code></pre>
<p>2.a is b与a==b 的区别</p>
<pre><code>a='vhcffh.com'
b='vhcffh.com'
a==b # True,a和b对应实例的内容是相同的
a is b # False,a和b指向不同的实例
b=a
a is b # True,a和b指向同一个实例
# 好奇怪啊!!!
In [43]: a='vhcffh.com'
In [44]: b='vhcffh.com'
In [45]: a==b
Out[45]: True
In [46]: a is b
Out[46]: False
In [47]: a='abcde'
In [48]: b='abcde'
In [49]: a == b
Out[49]: True
In [50]: a is b
Out[50]: True
</code></pre>
Linux常用命令
2019-05-21T00:00:00+00:00
2019-05-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-linuxchang-yong-ming-ling/
<p>Linux中的一些常用命令,tar</p>
<h2 id="1-tarjie-ya-da-bao-xiang-guan">1.tar解压打包相关</h2>
<pre data-lang="bash" class="language-bash "><code class="language-bash" data-lang="bash">tar -cvf log.tar log2019.log #仅打包,不压缩
tar -zcvf log.tar.gz log2019.log #打包后,以 gzip 压缩
tar -jcvf log.tar.bz2 log2019.log #打包后,以 bzip2 压缩
tar -ztvf log.tar.gz #gzip查阅
tar -jtvf log.tar.gz #bzip2查阅
tar -zxvf log.tar.gz #gzip解压
tar -jxvf log.tar.gz #bzip2解压
unzip log.zip -d dirname #zip解压到dirname目录
</code></pre>
<p>补充</p>
<p>c: 建立压缩档案<br />
-x:解压<br />
-t:查看内容<br />
-r:向压缩归档文件末尾追加文件<br />
-u:更新原压缩包中的文件</p>
<p>这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个。下面的参数是根据需要在压缩或解压档案时可选的。</p>
<p>-z:有gzip属性的<br />
-j:有bz2属性的<br />
-Z:有compress属性的<br />
-v:显示所有过程<br />
-O:将文件解开到标准输出</p>
<h2 id="2-wgetshe-zhi-dai-li">2.wget设置代理</h2>
<p>2.1环境变量中设置</p>
<pre><code>export http_proxy=http://127.0.0.1:8087
</code></pre>
<p>2.2使用配置文件</p>
<pre><code># cp /etc/wgetrc ~/.wgetrc
# vim ~/.wgetrc
# You can set the default proxies for Wget to use for http, https, and ftp.
# They will override the value in the environment.
https_proxy = http://127.0.0.1:8087/
http_proxy = http://127.0.0.1:8087/
ftp_proxy = http://127.0.0.1:8087/
# If you do not want to use proxy at all, set this to off.
use_proxy = on
</code></pre>
<h2 id="3-psxiang-guan-ming-ling">3.ps相关命令</h2>
<pre><code>ps -ef # 查看进程信息和执行的命令
</code></pre>
<h2 id="4-yong-hu-wen-jian-guan-li-xiang-guan">4.用户文件管理相关</h2>
<p>4.1Linux系统用户账号的管理</p>
<p>4.1.1添加新的用户账号</p>
<pre><code>useradd [-d dirname] username
# [-d dirname]指定用户主目录
</code></pre>
<p>4.1.2删除帐号</p>
<pre><code>userdel [-r] username
# [-R]把用户的主目录一起删除
</code></pre>
<p>4.1.3修改帐号</p>
<pre><code>usermod [] username
</code></pre>
<p>4.1.4用户密码管理</p>
<pre><code>passwd [-I][-u][-d][-f] username
# [-I]锁定密码,即禁用账号。
# [-u]密码解锁。
# [-d]使账号无密码。
# [-f]强迫用户下次登录时修改密码。
</code></pre>
<p>4.2Linux系统用户组的管理</p>
<p>4.2.1增加一个新的用户组</p>
<pre><code>groupadd [-g GID] username
# [-g GID]指定新用户组的组标识号(GID)
</code></pre>
<p>4.2.2删除一个已有的用户组</p>
<pre><code>groupdel groupname
</code></pre>
<p>4.2.3修改用户组的属性</p>
<pre><code>groupmod [-g GID] groupname
# [-g GID]指定新用户组的组标识号(GID)
</code></pre>
<p>4.2.4更改用户组</p>
<p><strong>如果一个用户同时属于多个用户组,那么用户可以在用户组之间切换,以便具有其他用户组的权限。用户可以在登录后,使用命令newgrp切换到其他用户组,这个命令的参数就是目的用户组。</strong></p>
<pre><code>newgrp root
</code></pre>
<p>4.3Linux文件用户属性修改</p>
<pre><code>chown [-R] user_name filename_OR_dirname # 更改文件或目录的所有者
chgrp [-R] group_name filename_OR_dirname # 更改文件或目录所在组
# [-R]参数递归更改目录下所有文件的用户属性
</code></pre>
<h2 id="can-kao-zi-liao">参考资料</h2>
<p>1.<a href="http://man.linuxde.net/tar">http://man.linuxde.net/tar</a></p>
<p>2.<a href="https://www.cnblogs.com/cloud2rain/archive/2013/03/22/2976337.html">https://www.cnblogs.com/cloud2rain/archive/2013/03/22/2976337.html</a></p>
<p>3.<a href="https://www.cnblogs.com/52php/p/5677628.html">https://www.cnblogs.com/52php/p/5677628.html</a></p>
<p>4.<a href="https://www.jb51.net/LINUXjishu/43356.html">https://www.jb51.net/LINUXjishu/43356.html</a></p>
UML图介绍
2019-04-21T00:00:00+00:00
2019-04-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-umltu-jie-shao/
<h1 id="1-yong-li-shi-tu">1.用例视图</h1>
<h2 id="1-1yong-li-tu">1.1用例图</h2>
<p>描述角色以及角色与用例之间的连接关系。说明的是谁要使用系统,以及他们使用该系统可以做些什么。</p>
<h1 id="2-she-ji-shi-tu">2.设计视图</h1>
<h2 id="2-1lei-tu">2.1类图</h2>
<p>根据用例图抽象成类,描述类的内部结构和类与类之间的关系,是一种静态结构图。
在UML类图中,常见的有以下几种关系: 泛化(Generalization),
实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency)。</p>
<p>类图是描述系统中的类,以及各个类之间的关系的静态视图。能够让我们在正确编写代码以前对系统有一个全面的认识。类图是一种模型类型,确切的说,是一种静态模型类型。</p>
<h3 id="2-2dui-xiang-tu">2.2对象图</h3>
<p>描述的是参与交互的各个对象在交互过程中某一时刻的状态。对象图可以被看作是类图在某一时刻的实例。</p>
<p>与类图极为相似,它是类图的实例,对象图显示类的多个对象实例,而不是实际的类。它描述的不是类之间的关系,而是对象之间的关系。</p>
<h1 id="3-jin-cheng-shi-tu">3.进程视图</h1>
<h2 id="3-1xu-lie-tu-shun-xu-tu">3.1序列图(顺序图)</h2>
<p>交互图的一种,描述了对象之间消息发送的先后顺序,强调时间顺序。</p>
<p>序列图是用来显示你的参与者如何以一系列顺序的步骤与系统的对象交互的模型。顺序图可以用来展示对象之间是如何进行交互的。顺序图将显示的重点放在消息序列上,即强调消息是如何在对象之间被发送和接收的。</p>
<h2 id="3-2xie-zuo-tu">3.2协作图</h2>
<p>交互图的一种,描述了收发消息的对象的组织关系,强调对象之间的合作关系。时序图按照时间顺序布图,而写作图按照空间结构布图</p>
<p>和序列图相似,显示对象间的动态合作关系。可以看成是类图和顺序图的交集,协作图建模对象或者角色,以及它们彼此之间是如何通信的。如果强调时间和顺序,则使用序列图;如果强调上下级关系,则选择协作图;这两种图合称为交互图。</p>
<h2 id="3-3zhuang-tai-tu">3.3状态图</h2>
<p>是一种由状态、变迁、事件和活动组成的状态机,用来描述类的对象所有可能的状态以及时间发生时状态的转移条件。</p>
<p>描述类的对象所有可能的状态,以及事件发生时状态的转移条件。可以捕获对象、子系统和系统的生命周期。他们可以告知一个对象可以拥有的状态,并且事件(如消息的接收、时间的流逝、错误、条件变为真等)会怎么随着时间的推移来影响这些状态。一个状态图应该连接到所有具有清晰的可标识状态和复杂行为的类;该图可以确定类的行为,以及该行为如何根据当前的状态变化,也可以展示哪些事件将会改变类的对象的状态。状态图是对类图的补充。</p>
<h2 id="3-4huo-dong-tu">3.4活动图</h2>
<p>是状态图的一种特殊情况,这些状态大都处于活动状态。本质是一种流程图,它描述了活动到活动的控制流。</p>
<p>描述用例要求所要进行的活动,以及活动间的约束关系,有利于识别并行活动。能够演示出系统中哪些地方存在功能</p>
<h1 id="4-shi-xian-shi-tu">4.实现视图</h1>
<h2 id="4-1gou-jian-tu-zu-jian-tu">4.1构件图 (组件图)</h2>
<p>描述代码构件的物理结构以及各种构建之间的依赖关系。用来建模软件的组件及其相互之间的关系,这些图由构件标记符和构件之间的关系构成。在组件图中,构件时软件单个组成部分,它可以是一个文件,产品、可执行文件和脚本等。</p>
<h1 id="5-tuo-bu-shi-tu">5.拓扑视图</h1>
<h2 id="5-1bu-shu-tu">5.1部署图</h2>
<p>是用来建模系统的物理部署。例如计算机和设备,以及它们之间是如何连接的。部署图的使用者是开发人员、系统集成人员和测试人员。</p>
<h1 id="6-ruan-jian-she-ji-ji-guo-cheng">6.软件设计及过程</h1>
<p>在需求阶段:采用用例图来描述需求<br />
在分析阶段:采用类图来描述静态结构<br />
在设计阶段:采用类图、包图对类的接口进行设计<br />
在实现阶段:将类用某个面向对象的语言实现<br />
在集成与交付阶段:构件图、包图、部署图<br />
在测试阶段:单元测试使用类图和类的规格说明书<br />
集成测试阶段使用类图、包图、构件图和合作图<br />
系统测试使用用例图来测试系统功能</p>
边缘计算系统中延迟敏感任务的成本有效调度
2019-04-05T00:00:00+00:00
2019-04-05T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-bian-yuan-ji-suan-xi-tong-zhong-yan-chi-min-gan-ren-wu-de-cheng-ben-you-xiao-diao-du/
<h2 id="zhai-yao">摘要</h2>
<p>边缘计算作为一种新兴的计算模型,可以将具有有限计算资源和能量的物联网(IoT)设备的延迟敏感计算任务卸载到边缘云。在边缘计算系统中,多个服务器放置在IoT设备附近的网络边缘上以处理卸载的任务。边缘计算系统的一个关键问题是如何在完成卸载任务的同时降低系统成本。在本文中,我们研究任务调度问题,以降低边缘计算系统的成本。我们将任务调度问题建模为优化问题,其目标是在满足所有任务的延迟要求的同时最小化系统成本。然后,我们证明了所提出的优化问题是NP难的。为了有效地解决这个优化问题,我们提出了一种任务调度算法,称为两阶段任务调度成本优化(TTSCO)。我们通过与最优解进行比较来验证算法的有效性。结果表明,对于我们使用的95%的数据集,近似比率小于1.2。性能评估表明,该算法能够有效降低边缘计算系统的成本,同时满足所有任务的延迟要求。索引术语</p>
<ul>
<li>边缘计算;任务调度;延迟敏感的任务;成本效益;</li>
</ul>
<h2 id="yun-ji-suan-yu-bian-yuan-ji-suan-de-qu-bie-he-bian-yuan-ji-suan-yao-jie-jue-de-wen-ti">云计算与边缘计算的区别和边缘计算要解决的问题</h2>
<p>随着物联网技术的发展,延迟敏感应用(例如,健康监测[1],基于位置的增强现实游戏)的数量正在迅速增加[2]。由于物联网设备的计算资源和能量有限,因此应将许多处理繁重的任务卸载到远程服务器进行处理。具有强大计算能力的云计算被认为是处理卸载任务的潜在方式。但是,由于传统云和物联网设备之间的距离较远,将大量任务发送到传统云进行处理会导致响应时间过长,网络拥塞严重。为了解决这个问题,最近提出边缘计算作为一种有前景的计算模型[3],[4]。边缘计算提供附加的计算基础设施层,其由网络边缘处的一些服务器(即,基站)组成。对于从物联网设备卸载的计算任务,边缘计算提供计算服务并将结果返回给设备。这样,卸载任务的传输延迟和核心网的流量负载将大大降低。</p>
<h2 id="zai-bian-yuan-ji-suan-zhong-de-ren-wu-diao-du-wen-ti">在边缘计算中的任务调度问题</h2>
<p>设备如何进行任务卸载决策
降低传输或计算任务所产生的系统成本
边缘计算系统中任务调度问题的成本优化
在边缘计算中,任务调度问题已成为研究的热门话题[9] - [18]。为了减少由任务计算引起的能耗,物联网设备会将计算任务卸载到边缘服务器。但是,卸载任务会消耗额外的能量来将任务传输到边缘服务器,卸载任务的完成时间也会增加。在这种情况下,一些工作研究了设备如何进行任务卸载决策[9] - [12]。另外,当计算任务被调度到不同的边缘服务器时,传输和计算的成本也不同。因此,一些工作旨在降低传输或计算任务所产生的系统成本[13] - [18]。但是,这些工作很少考虑边缘服务器生成的成本。在非高峰时间(例如,在夜间)降低由服务器引起的系统成本的问题在很大程度上未被探索[5]。</p>
<h2 id="ben-wen-yan-jiu-de-wen-ti">本文研究的问题</h2>
<p>边缘计算系统中任务调度问题的成本优化,在本文中,我们研究边缘计算系统中任务调度问题的成本优化。目标是在满足所有任务的QoS要求的同时最小化边缘计算系统的成本。我们为延迟敏感的输入任务开发了一个任务调度模型。然后,我们制定成本优化问题并证明该优化问题为NP-Hard。接下来,我们提出了一种近似算法来解决这个优化问题,称为两阶段任务调度成本优化(TTSCO),并对TTSCO算法进行分析。最后,仿真结果验证了算法的准确性和性能。</p>
<p>边缘计算作为一种新兴的计算模型,可以将具有有限计算资源和能量的物联网(IoT)设备的延迟敏感计算任务卸载到边缘云。在边缘计算系统中,多个服务器放置在IoT设备附近的网络边缘上以处理卸载的任务。</p>
java注解的使用
2019-03-20T00:00:00+00:00
2019-03-20T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-javazhu-jie-de-shi-yong/
<h3 id="1-san-chong-biao-zhun-zhu-jie">1.三种标准注解</h3>
<ul>
<li>
<p>@Override,表示当前的方法定义覆盖了父类中的方法。必须要有相同的方法签名即(方法名,参数类型,参数顺序,参数个数)都一样。否则在编译过程中发出错误提示。</p>
</li>
<li>
<p>@Deprecated,对不应该再使用的方法添加注解,当使用这个方法的时候,会在编译时候显示提示信息。</p>
</li>
<li>
<p>@SuppressWarnings,关闭不当的编译器报警信息</p>
</li>
</ul>
<h3 id="2-si-chong-yuan-zhu-jie">2.四种元注解:</h3>
<ul>
<li>
<p>@Target,表示该注解可以用什么地方。如CONSTRUCTOR,构造器声明;FIELD,域声明;METHOD,方法声明;TYPE,类,接口或enum声明</p>
</li>
<li>
<p>@Retention,表示需要在什么级别保存该注解信息。如SOURCE,注解将被编译器丢弃;CLASS,注解在class文件可用,但会被VM丢弃RUNTIME,VM将在运行期间也保留注解,可以使用反射机制读取注解信息</p>
</li>
<li>
<p>@Documented,将此注解包含到Javadoc中。</p>
</li>
<li>
<p>@Inherited,允许子类继承父类的注解。</p>
</li>
</ul>
<p>Java 7开始,额外添加了 3 个注解:</p>
<ul>
<li>
<p>@SafeVarargs - Java 7
开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生</p>
</li>
<li>
<p>@FunctionalInterface - Java 8
开始支持,标识一个匿名函数或函数式接口。</p>
</li>
<li>
<p>@Repeatable - Java 8
开始支持,标识某注解可以在同一个声明上使用多次。</p>
</li>
</ul>
数据可视化开源项目
2019-03-10T00:00:00+00:00
2019-03-10T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2019/hexo-shu-ju-ke-shi-hua-kai-yuan-xiang-mu/
<h3 id="1-d3">1.<a href="https://d3js.org/">D3</a></h3>
<p>github地址:<a href="https://github.com/d3/d3">https://github.com/d3/d3</a></p>
<p><img src="https://blog.vhcffh.com/2019/hexo-shu-ju-ke-shi-hua-kai-yuan-xiang-mu/8_10_d3.png" alt="d3" /></p>
<h3 id="2-chartjs">2.<a href="https://www.chartjs.org">chartjs</a></h3>
<p>github地址:<a href="https://github.com/chartjs/Chart.js">https://github.com/chartjs/Chart.js</a></p>
<p><img src="https://blog.vhcffh.com/2019/hexo-shu-ju-ke-shi-hua-kai-yuan-xiang-mu/8_10_charts.png" alt="charts" /></p>
<h3 id="3-leafletjs">3.<a href="https://leafletjs.com/examples.html">leafletjs</a></h3>
<p>github地址:<a href="https://github.com/Leaflet/Leaflet">https://github.com/Leaflet/Leaflet</a></p>
<p>主要是移动端地图的支持</p>
<p><img src="https://blog.vhcffh.com/2019/hexo-shu-ju-ke-shi-hua-kai-yuan-xiang-mu/8_10_leafletjs.png" alt="leafletjs" /></p>
<h3 id="4-echarts">4.<a href="https://echarts.baidu.com/examples/#chart-type-line">echarts</a></h3>
<p>百度家的</p>
<p>github地址:<a href="https://github.com/ecomfe/echarts">https://github.com/ecomfe/echarts</a></p>
<p><img src="https://blog.vhcffh.com/2019/hexo-shu-ju-ke-shi-hua-kai-yuan-xiang-mu/8_10_echarts.png" alt="echarts" /></p>
<h3 id="5-chartist-js">5.<a href="https://gionkunz.github.io/chartist-js/examples.html">Chartist-js</a></h3>
<h3 id="6-sigmajs">6.<a href="http://sigmajs.org/">sigmajs</a></h3>
<h3 id="7-metricsgraphicsjs">7.<a href="https://metricsgraphicsjs.org/examples.htm">metricsgraphicsjs</a></h3>
<h3 id="8-dc-js-kai-yuan-d3">8.<a href="https://dc-js.github.io/dc.js/">DC.js,开源(D3)</a></h3>
<h3 id="9-a-li-shu-ju-ke-shi-hua">9.<a href="https://antv.alipay.com/zh-cn/index.html">阿里数据可视化</a></h3>
<p><a href="https://antv.alipay.com/zh-cn/g2/3.x/demo/index.html">G2</a>,<a href="https://antv.alipay.com/zh-cn/g6/2.x/demo/index.html">G6</a>,<a href="https://antv.alipay.com/zh-cn/f2/3.x/demo/index.html">F2</a>,<a href="https://antv.alipay.com/zh-cn/l7/1.x/demo/index.html">L7</a></p>
简单的iptables配置过程
2018-10-19T00:00:00+00:00
2018-10-19T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-jian-dan-de-iptablespei-zhi-guo-cheng/
<p>简单的记一下iptables的配置过程,以后用到了就不用再google了<br />
总的来说分为四步,清楚规则,预设规则,添加自定义规则,保存规则</p>
<h2 id="iptablespei-zhi-guo-cheng">iptables配置过程</h2>
<p>1.清除规则:</p>
<p>清楚旧的规则<br />
iptables -F 清除预设表filter中的所有规则链的规则<br />
iptables -X 清除预设表filter中使用者自定链中的规则</p>
<p>2.设定预设规则</p>
<p>默认情况下对各种包的处理方式<br />
iptables -p INPUT DROP<br />
iptables -p OUTPUT ACCEPT<br />
iptables -p FORWARD DROP</p>
<p>3.添加用户自定义规则</p>
<p>对于特定端口协议的包的规则进行设置<br />
iptables -A INPUT -p tcp ---dport 22 -j ACCEPT<br />
-A:表示添加规则到INPUT(OUTPUT,FORWARD)链<br />
-p:表示tcp(udp)协议<br />
---dport(---sport):目的端口号(源端口号)<br />
-j():添加规则为接受</p>
<p>4.保存规则</p>
<p>不保存的话重启后就没有了<br />
service iptables save</p>
<p>参考资料</p>
<p>1.<a href="http://www.cnblogs.com/JemBai/archive/2009/03/19/1416364.html">http://www.cnblogs.com/JemBai/archive/2009/03/19/1416364.html</a></p>
socket网络编程
2018-10-18T00:00:00+00:00
2018-10-18T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-socketwang-luo-bian-cheng/
<h3 id="socketwang-luo-bian-cheng">socket网络编程</h3>
<pre><code>int inet_aton(const char *cp, struct in_addr *inp);
</code></pre>
<p>转换网络主机地址为二进制数值并存储与第二个参数中<br />
函数返回0表示主机地址无效,非0表示主机地址有效<br />
转化完后需要调用htons或htonl函数才能将主机字节序转换为网络字节序用于网络传输</p>
<pre><code>char *inet_ntoa(struct in_addr in);
</code></pre>
<p>转换网络字节序为标准的ASCII以点分开的地址,函数返回字符串指针<br />
该字符串空间为静态分配,第二次调用时会覆盖第一次的内容</p>
<pre><code>in_addr_t inet_addr(const char *cp);
</code></pre>
<p>转换网络主机地址为网络字节序二进制值<br />
参数无效,返回-1(INADDR_NONE)<br />
注意:转换255.255.255.255时也返回-1</p>
<pre><code>int inet_pton(int af, const char *src, void *dst);
</code></pre>
<p>转换字符串到网络地址,af是地址簇,<em>src是来源地址,</em>
dst接收转换后的数据。</p>
<pre><code>const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
</code></pre>
<p>转换网络字节序二进制值到ASCII类型的地址,参数的作用和inet_pton相同,<br />
socklen_t
cnt指所指向缓存区dst的大小,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC。</p>
<pre><code>int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout);
</code></pre>
<p>int maxfdp
指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。<br />
struct fd_set
可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄。监视这些文件描述符的读变化</p>
<pre><code>int setsockopt(SOCKET s,int level,int optname,const char* optval,int optlen);
</code></pre>
<p>s(套接字): 指向一个打开的套接口描述字<br />
level:(级别): 指定选项代码的类型。<br />
SOL_SOCKET: 基本套接口<br />
IPPROTO_IP: IPv4套接口<br />
IPPROTO_IPV6: IPv6套接口<br />
IPPROTO_TCP: TCP套接口<br />
optname(选项名): 选项名称<br />
optval(选项值): 是一个指向变量的指针 类型:整形,套接口结构,
其他结构类型:linger{}, timeval{ }<br />
optlen(选项长度) :optval 的大小</p>
<pre><code>int PASCAL FAR recvfrom( SOCKET s, char FAR* buf, int len, int flags,struct sockaddr FAR* from, int FAR* fromlen);
</code></pre>
<p>s:标识一个已连接套接口的描述字。<br />
buf:接收数据缓冲区。<br />
len:缓冲区长度。<br />
flags:调用操作方式。<br />
from:(可选)指针,指向装有源地址的缓冲区。<br />
fromlen:(可选)指针,指向from缓冲区长度值。</p>
<h3 id="can-kao-zi-liao">参考资料</h3>
<p>1.<a href="https://blog.csdn.net/zyy617532750/article/details/58595700">https://blog.csdn.net/zyy617532750/article/details/58595700</a><br />
2.<a href="https://www.cnblogs.com/zhoudingcocng/p/6209961.html">https://www.cnblogs.com/zhoudingcocng/p/6209961.html</a></p>
形式语言与自动机基础知识
2018-09-22T00:00:00+00:00
2018-09-22T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-xing-shi-yu-yan-yu-zi-dong-ji-ji-chu-zhi-shi/
<p>形式语言与自动机这门课需要有离散数学的基础,但本科通信工程里没有学过这门课,总结一些这门课中需要的基础知识</p>
<h1 id="1-ji-he-ji-qi-yun-suan">1.集合及其运算</h1>
<h2 id="1-1zi-ji-he-zhen-zi-ji">1.1子集和真子集</h2>
<p>$A$子集:$A \subseteq B$或$B \subseteq A$<br />
$A$是$B$的真子集:$A \subset B$或$B \subset A$<br />
$x$是$A$的一个元素:$x \in A$<br />
$x$不是$A$的一个元素:$x \notin A$</p>
<h2 id="1-2ji-he-de-jiao-bing-chai-bu">1.2集合的交并差补</h2>
<p>$A$和$B$的并集:$A\cup B={x|x\in A或x\in B}$<br />
$A$和$B$的交集:$A\cap B={x|x\in A且x\in B}$<br />
$A$和$B$的差集:$A - B={x|x\in A且x\notin B}$<br />
若$B\subseteq
A$我们也称$A−B$为$B$的(关于$A$)补,记作:$\overline B(A)$</p>
<h2 id="1-3ji-he-de-bing-de-tui-yan">1.3集合的并的推广</h2>
<p>设$I$是某些标号的集合我们将$\displaystyle \bigcup_{i\in
I}A_i={x|存在i\in I,使得x\in A_i}$</p>
<h2 id="1-4ade-mi-ji">1.4A的幂集</h2>
<p>$A$的所有子集的集合,记作$2^A={B|B\subseteq A}$</p>
<h2 id="1-5di-qia-er-cheng-ji">1.5笛卡尔乘积</h2>
<p>$A\times B={(a,b)|a\in A且b\in B}$</p>
<h2 id="1-6ji-he-zhi-jian-de-guan-xi">1.6集合之间的关系</h2>
<p>由$A$到$B$的关系是$A×B$的任何子集。若$A=B$,则称为$A$上的关系。若$R$为$A$到$B$的关系,当$(a,b)$在$R$内时,可写成$aRb$</p>
<h2 id="1-7ji-he-guan-xi-de-xing-zhi">1.7集合关系的性质</h2>
<p>设$R$是集合$A$上的关系,则有<br />
(1)若对$A$中的任一元素$a$,都有$aRa$,则称$R$是<strong>自反的</strong>;<br />
(2)若对$A$中的任何元素$a,b$,从$aRb$能够推到出$bRa$,则称$R$是<strong>对称的</strong>;<br />
(3)若$a,b,c$是$A$中的元素,从$aRb$和$bRc$能够推出$aRc$,则称$R$是<strong>传递的</strong>;<br />
若关系$R$同时是自反的,对称的和传递的,则称之为<strong>等价关系</strong>。<br />
2018-09-22 15:28:12</p>
<h1 id="can-kao-zi-liao">参考资料</h1>
<hr />
<p>1.《形式语言与自动机》陈有祺编著</p>
梯度旋度和散度
2018-09-08T00:00:00+00:00
2018-09-08T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-ti-du-xuan-du-he-san-du/
<p>有关梯度旋度和散度的定义和计算,记录一下</p>
<h2 id="1-ding-yi">1.定义</h2>
<h3 id="1-1ti-du">1.1梯度</h3>
<p>设函数$ u=f(x,y,z) $在空间区域$ G $内具有一阶连续偏导数,其中点$ P(x,y,z) \in G $
则向量</p>
<p>$$
\left(
\frac {\partial f}{\partial x},
\frac {\partial f}{\partial y},
\frac {\partial f}{\partial z}
\right)=
\frac {\partial f}{\partial x}\vec i+
\frac {\partial f}{\partial y}\vec j+
\frac {\partial f}{\partial z}\vec k
$$</p>
<p>为函数$ u=f(x,y,z) $在点$ P(x,y,z) $的<strong>梯度</strong></p>
<p>记为$ grad;f(x,y,z) $ 或$ \nabla f(x,y,z) $ </p>
<p>(<font color="red">注</font>: $ \nabla = \frac {\partial}{\partial x}\vec i+\frac {\partial}{\partial y}\vec j+\frac {\partial}{\partial z}\vec k $为三维的向量微分算子)</p>
<h3 id="1-2xuan-du">1.2旋度</h3>
<p>在三维空间$ G $ 中有三维直角坐标系$ O_{xyz} $,设向量场:</p>
<p>$$
\vec v=v_x\vec i+v_y\vec j+v_z\vec k
$$</p>
<p>其中$ v_x,v_y,v_z $具有一阶连续偏导数,点$ P(x,y,z) \in G $</p>
<p>向量</p>
<p>$$
\begin{vmatrix}
\vec i & \vec j & \vec k \cr
\frac {\partial}{\partial x} & \frac {\partial}{\partial y} & \frac {\partial}{\partial z} \cr
v_x & v_y & v_z \cr
\end{vmatrix} =
(\frac {\partial v_z}{\partial y} - \frac {\partial v_y}{\partial z})\vec i+
(\frac {\partial v_x}{\partial z} - \frac {\partial v_z}{\partial x})\vec j+
(\frac {\partial v_y}{\partial x} - \frac {\partial v_x}{\partial y})\vec k
$$</p>
<p>为向量场$ \vec v $ 在点$ P(x,y,z) $的<strong>旋度</strong></p>
<p>记为$ curl;v $或者$ \nabla \times v $</p>
<h3 id="1-3san-du">1.3散度</h3>
<p>在三维空间$ G $ 中有三维直角坐标系$ O_{xyz} $,设向量场:</p>
<p>$$
\vec v=v_x\vec i+v_y\vec j+v_z\vec k
$$</p>
<p>其中$ v_x,v_y,v_z $具有一阶连续偏导数,点$ P(x,y,z) \in G $</p>
<p>标量</p>
<p>$$
\frac {\partial v_x}{\partial x}+
\frac {\partial v_y}{\partial y}+
\frac {\partial v_z}{\partial z}
$$</p>
<p>为向量场$$在点$$的散度</p>
<p>记为$div;v$或$\nabla \cdot v$</p>
vim的使用
2018-06-23T00:00:00+00:00
2018-06-23T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-vimde-shi-yong/
<p>1.光标移动</p>
<p><code>h</code>,<code>j</code>,<code>k</code>,<code>l</code>(空格):向左,下,上,右移动光标</p>
<p><code>Ctrl</code>+<code>(f,b,d,u)</code>:屏幕向下移一页,向上移一页,向下移半页,向上移半页<br />
+-:下一行,上一行<br />
<code>H</code>,<code>M</code>,<code>L</code>:光标移动到这个屏幕的最上方,中央,最下方那一行的第一个字符<br />
<code>G</code>,<code>nG</code>,<code>gg</code>:移动都文档末,移动到第n行,移动到第一行<br />
<code>n</code>:向下移动n行</p>
<p>2.搜索替换</p>
<p><code>/word</code>:向光标之下寻找word<br />
<code>?word</code>:向光标之上寻找word<br />
<code>n</code>,<code>N</code>:重复搜索(不变方向,改变方向)</p>
<p>参考资料</p>
<p>1.<a href="http://www.runoob.com/linux/linux-vim.html">http://www.runoob.com/linux/linux-vim.html</a></p>
python类和实例的一些属性
2018-05-17T00:00:00+00:00
2018-05-17T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-pythonlei-he-shi-li-de-yi-xie-shu-xing/
<h2 id="1-selfhe-init">1.self和__init__()</h2>
<p>self代表类的实例,如下:</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> class Test:
a,b='classa','classb'#类的属性
def __init__(self):
self.b='selfb'#实例的属性
self.c='selfc'
print(Test.a)
print(Test.b)
#-output:classa classb-访问类的属性
a=Test()
print(a.a)
#-output:classa-通过实例访问类的属性
print(a.b)
#-output:selfb-当实例和类都具有某属性时,输出实例属性
print(a.c)
print(Test.c)
#output
</code></pre>
<p>当实例和类有相同的属性时,如何通过实例访问类的属性呢?<br />
可以通过__class__访问</p>
<h2 id="2-class">2.<strong>class</strong></h2>
<p>__class__是指实例所对应的类</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> # -*- coding: utf-8 -*-
class Test:
a='classa'
print(count)
def __init__(self):
self.a='selfa'
print(self)
#-output:<__main__.Test object at 0x055A1270>
print(self.__class__)
#-output:<class '__main__.Test'>
a=Test()
print('实例a',a)
#-output:<__main__.Test object at 0x055A1270>
print('类Test',Test)
#-output:<class '__main__.Test'>
print(a.a,a.__class__.a)
#-output:selfa classa
</code></pre>
<p>通过__init__()可以实现对类的实例的统计</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> # -*- coding: utf-8 -*-
class Test(object):
count=0
def __init__(self):
super(Test, self).__init__()
self.__class__.count+=1
if __name__=="__main__":
a=Test()
print(Test.count)
b=Test()
print(Test.count)
Test()
print(Test.count)
</code></pre>
<p>输出为</p>
<pre><code>1
2
3
</code></pre>
<p>每创建一个Test类的实例,Test.count都加1</p>
<h2 id="3-dict">3.<strong>dict</strong></h2>
<p>__dict__是一个字典,键是属性名,值为属性值。<br />
类有自己的__dict__,类的实例也有自己的__dict__</p>
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> # -*- coding: utf-8 -*-
class Test(object):
classa='classa'
def __init__(self):
super(Test, self).__init__()
self.selfb='selfb'
if __name__=="__main__":
print(Test.__dict__)
a=Test()
print(a.__dict__)
</code></pre>
<p>输出为</p>
<pre><code>{'__module__': '__main__', 'classa': 'classa', '__init__': <function Test.__init__ at 0x04D03468>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
{'selfb': 'selfb'}
</code></pre>
<p>可见类Test有classa属性以及一些其他属性<br />
类Test的实例a只有self.b属性</p>
<h2 id="4-len-self-han-shu">4.<strong>len</strong>(self)函数</h2>
<p>返回元素个数,实现len()方法。即类实现了__len__()函数就可以使用len()函数</p>
<h2 id="5-getitem-self-key">5.<strong>getitem</strong>(self,key)</h2>
<p>实现字典一样的功能,当类的实例对象(假设为P)调用P[key]时,调用此函数返回。</p>
<h2 id="can-kao-zi-liao">参考资料</h2>
<p>1,<a href="https://docs.python.org/">https://docs.python.org</a></p>
python中关于路径的知识
2018-05-17T00:00:00+00:00
2018-05-17T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-pythonzhong-guan-yu-lu-jing-de-zhi-shi/
<pre data-lang="python" class="language-python "><code class="language-python" data-lang="python"> os.getcwd()
#输出当前路径
os.listdir()
#输出当前路径下的所有文件夹名和文件名
os.remove('filename.xxx')和os.unlink('filename.xxx')功能一样
#删除文件filename.xxx
os.rmdir('path')
#删除目录(目录必须为空)
os.removedirs('p1//p2//p3')
#依次删除目录p3,p2,p1直到某一目录不为空
os.chdir('path')
#更改当前路径
</code></pre>
<p>参考资料</p>
<p>1.<a href="https://www.cnblogs.com/yanglang/p/7610838.html">https://www.cnblogs.com/yanglang/p/7610838.html</a><br />
2.<a href="https://blog.csdn.net/muwinter/article/details/77196261">https://blog.csdn.net/muwinter/article/details/77196261</a></p>
python3之模块
2018-04-28T00:00:00+00:00
2018-04-28T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-python3zhi-mo-kuai/
<h2 id="1-python3mo-kuai">1.python3模块</h2>
<p>简单讲,一个模块就是一个*.py文件,这个文件里面可以定义类,函数,变量,也可以包含可执行代码。</p>
<h2 id="2-mo-kuai-de-yin-ru">2.模块的引入</h2>
<p>2.1import引入</p>
<pre><code>import module1,[module2[,.........moduleN]]
</code></pre>
<p>一个模块只会被导入一次,不管执行了多少次import,这样可以防止导入模块被重复执行。</p>
<p>2.2from * import语句</p>
<pre><code>from modname import name1,name2..........nameN
from mod import func_1
</code></pre>
<p>这个声明不会把整个mod模块导入到当前命名空间中,它只会将mod里的func_1单个引入到执行这个声明的模块的全局符号表。</p>
<p>from ... import *
:这样会把模块中的所有内容导入到当前命名空间,一般不建议使用,消耗内存空间,也容易出现未预知的问题。</p>
<h2 id="3-sou-suo-lu-jing">3.搜索路径</h2>
<p>搜索顺序:当前目录->系统环境变量PATHONHOME->标准链接库目录<br />
通过<code>sys.path</code>可查看搜索路径</p>
<pre><code>>>> import sys
>>> sys.path
['', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36', 'C:\\Python36\\lib\\site-packages']
</code></pre>
<h2 id="4-ming-ming-kong-jian">4.命名空间</h2>
<p>变量是拥有匹配对象的名字(标识符)。命名空间是一个包含了变量名称(键)和它们各自相应的对象们(值)的字典。<br />
函数内用到全局变量要用global语句,否则会当作局部变量处理</p>
<h2 id="qi-ta-wen-ti">其他问题:</h2>
<p>1.python模块里的可执行代码何时执行?<br />
当导入模块时,python顶层的代码会执行一次,若要重新执行模块的顶层代码需要通道reload()函数<br />
另外python3中的reload()需要从imp包中导入<br />
2.关于python模块循环引入的问题?</p>
<h2 id="can-kao-zi-liao">参考资料</h2>
<p>1.<a href="https://www.cnblogs.com/zhangxinqi/p/7905103.html">https://www.cnblogs.com/zhangxinqi/p/7905103.html</a></p>
DEBUG常用功能
2018-02-12T00:00:00+00:00
2018-02-12T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-debugchang-yong-gong-neng/
<p>window 10中DEBUG的安装,以及DEBUG调试中一些常用的命令</p>
<h3 id="1-an-zhuang">1.安装</h3>
<p>window 10中好像已经没有了Debug的调试工具,需要下载另外的软件</p>
<p>下载安装DOSBox.百度云下载地址:(DOSBOX)[<a href="https://pan.baidu.com/s/1cC3cuy">https://pan.baidu.com/s/1cC3cuy</a>]<br />
下载后先安装DOSBox0.74-win32-installer.exe<br />
然后找到文件dos-box0.74.conf(C:\Users\username\AppData\Local\DOSBox)<br />
添加两行</p>
<pre><code>MOUNT C E:\DEBUG # 将目录E:\DEBUG挂载为DOSBOX下的C:
set PATH=$PATH$;E:\DEBUG # 将E:\DEBUG写入环境变量PATH中
</code></pre>
<p>并将下载的MASM.exe,LINK.exe,debug.exe三个文件放入目录E:\DEBUG<br />
打开软件,输入<code>c:</code>就可以使用debug命令了</p>
<h3 id="2-chang-yong-ming-ling">2.常用命令:</h3>
<p>R命令:查看改变CPU寄存器的内容<br />
D命令:查看内存中的内容<br />
E命令:改写内存中的内容<br />
U命令:将内存中的机器指令翻译成汇编指令<br />
T命令:执行一条机器指令<br />
A命令:以汇编指令的格式在内存中写入一条机器指令<br />
Q命令:退出DEBUG调试</p>
<h3 id="3-ming-ling-ju-ti-shi-yong-shi-li">3.命令具体使用实例</h3>
<h4 id="3-1rming-ling">3.1R命令</h4>
<p>R命令:查看改变CPU寄存器的内容</p>
<pre><code>-r ;显示寄存器的值
-r reg ;改变寄存器reg的值
</code></pre>
<p><img src="https://blog.vhcffh.com/2018/hexo-debugchang-yong-gong-neng/2_12_R%E5%91%BD%E4%BB%A4.PNG" alt="img" /></p>
<h4 id="3-2dming-ling">3.2D命令</h4>
<p>D命令:查看内存中的内容<br />
默认显示128字节的内容</p>
<pre><code>-d ;默认地址
-d 段地址:偏移地址 ;指定地址
-d 段地址:偏一偏二 ;两个地址间的内容
</code></pre>
<p><img src="https://blog.vhcffh.com/2018/hexo-debugchang-yong-gong-neng/2_12_D%E5%91%BD%E4%BB%A4.PNG" alt="img" /></p>
<h4 id="3-3eming-ling">3.3E命令</h4>
<p>E命令:改写内存中的内容</p>
<pre><code>-e 起始地址 数据 数据 ...
-e 起始地址
</code></pre>
<p><img src="https://blog.vhcffh.com/2018/hexo-debugchang-yong-gong-neng/2_12_E%E5%91%BD%E4%BB%A4.PNG" alt="img" /></p>
<h4 id="3-4uming-ling">3.4U命令</h4>
<p>U命令:将内存中的机器指令翻译成汇编指令<br />
与D命令有些类似<br />
<img src="https://blog.vhcffh.com/2018/hexo-debugchang-yong-gong-neng/2_12_U%E5%91%BD%E4%BB%A4.PNG" alt="img" /></p>
<h4 id="3-5tming-ling">3.5T命令</h4>
<p>T命令:执行内存中的一条机器指令<br />
指令位置由cs:ip确定<br />
<img src="https://blog.vhcffh.com/2018/hexo-debugchang-yong-gong-neng/2_12_T%E5%91%BD%E4%BB%A4.PNG" alt="img" /></p>
<h4 id="3-6aming-ling">3.6A命令</h4>
<p>A命令:以汇编指令的格式在内存中写入一条机器指令<br />
<img src="https://blog.vhcffh.com/2018/hexo-debugchang-yong-gong-neng/2_12_A%E5%91%BD%E4%BB%A4.PNG" alt="img" /></p>
Git使用中的一些问题
2018-01-22T00:00:00+00:00
2018-01-22T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-gitshi-yong-zhong-de-yi-xie-wen-ti/
<h3 id="1-pei-zhi-wen-ti">1-配置问题</h3>
<p>全局配置</p>
<pre><code>$ git config --list 查看当前用户信息
$ git config --global user.name "username" 配置用户名
$ git config --global user.email emailname@example.com 配置邮箱
</code></pre>
<p>项目配置(进入项目目录)</p>
<pre><code>$ cat .git/config 项目配置信息
$ git config user.name "username" 配置用户名
$ git config user.email emailname@example.com 配置邮箱
</code></pre>
<p>全局的配置就是加上---global<br />
项目未设置的配置默认使用全局配置</p>
<h3 id="2-mi-yao-wen-ti">2-密钥问题</h3>
<pre><code>$ cd ~/.ssh 查看是否存在密钥
$ ssh-keygen -t rsa -C "emailname@example.com" 生成密钥
$ ssh -T git@github.com 测试是否配置成功
</code></pre>
<p>生成密钥过程中回车三次,是密码为空,不然每次push都要输入密码<br />
生成密钥后主要有两个文件<br />
~/.ssh/id_rsa<br />
私钥进行处理后的一些内容<br />
~/.ssh/id_rsa.pub<br />
公钥进行处理后的内容,提交到服务器(github或coding)的内容<br />
~/.ssh/known_hosts<br />
这个文件可能会有,是ssh对服务器的一些记录</p>
<h3 id="3-gong-zuo-qu-zan-cun-qu-he-ban-ben-ku">3-工作区、暂存区和版本库</h3>
<p><img src="https://blog.vhcffh.com/2018/hexo-gitshi-yong-zhong-de-yi-xie-wen-ti/git.jpg" alt="img" /></p>
<p><code>git add</code>:暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。</p>
<p><code>git commit</code>:暂存区的目录树写到版本库(对象库)中,master
分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。</p>
<p><code>git reset HEAD</code> :暂存区的目录树会被重写,被 master
分支指向的目录树所替换,但是工作区不受影响。</p>
<p><code>git rm --cached <file></code>:直接从暂存区删除文件,工作区则不做出改变。</p>
<p><code>git checkout .</code> 或者
<code>git checkout -- <file></code>:用暂存区全部或指定的文件替换工作区的文件。<strong>这个操作很危险,会清除工作区中未添加到暂存区的改动。</strong></p>
<p><code>git checkout HEAD .</code>或者 <code>git checkout HEAD <file></code>:用 HEAD 指向的
master
分支中的全部或者部分文件替换暂存区和以及工作区中的文件。<strong>这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。</strong></p>
<h3 id="can-kao-zi-liao">参考资料</h3>
<p>1.<a href="https://www.cnblogs.com/hustskyking/p/problems-in-git-when-ssh.html">https://www.cnblogs.com/hustskyking/p/problems-in-git-when-ssh.html</a></p>
简单了解汇编
2018-01-11T00:00:00+00:00
2018-01-11T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2018/hexo-jian-dan-liao-jie-hui-bian/
<h3 id="1-hui-bian-shi-shi-yao">1.汇编是什么</h3>
<p>汇编语言的主体是汇编指令,汇编指令是由机器指令发展而来,例如:<br />
操作:将寄存器bx的内容送到ax<br />
机器指令:101100000000000000000011<br />
记起来太过于复杂,从而有了替代它的<br />
汇编指令:<code>mov ax,bx</code><br />
方便记忆,也符合人们的逻辑思维<br />
程序员们编写汇编指令,在通过编译器编译成机器指令,然后就可以在计算机中运行了(如今的大部分高级语言是通过编译器,将高级语言转换成汇编语言,在编译成机器语言在电脑中运行)<br />
不管什么语言,都要转换成机器语言在电脑中运行,汇编语言和高级语言都是人们为了简化程序制作过程而已</p>
<h3 id="2-ji-suan-ji-de-zong-xian">2.计算机的总线</h3>
<p>从逻辑上总线分为三类(8086)<br />
数据总线:传送数据,总线宽度决定一次读取数据多少(16)<br />
地址总线:指定地址,总线宽度决定寻址能力(20)<br />
控制总线:控制读写</p>
<h3 id="3-cun-chu-qi">3.存储器</h3>
<p>RAM:程序和数据都要加载到RAM即内存中,才能通过CPU运行,可读可写<br />
ROM:一些芯片的主要参数,及操作指令,芯片出厂时已经固定,仅可读<br />
计算机运行时,全部程序和数据都要加载到RAM中<br />
通过ROM中的一些必要信息,CPU才能对外设进行操作</p>
python遇到的错误
2017-12-26T00:00:00+00:00
2017-12-26T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-pythonyu-dao-de-cuo-wu/
<p>python使用在中遇到的错误</p>
<p>1.DLL load failed</p>
<p>python ImportError: DLL load failed: %1 不是有效的 Win32 应用程序<br />
解决方法:去下载与你所安装的Python版本对应的pywin32并安装<br />
下载链接:<a href="https://sourceforge.net/projects/pywin32/files/pywin32/">https://sourceforge.net/projects/pywin32/files/pywin32/</a><br />
对应版本和位数<br />
Python 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:42:59) [MSC v.1500
32 bit (Intel)] on win32<br />
Type "help", "copyright", "credits" or "license" for more information.<br />
还有一种可能是导入的模块是64位的,而你的python是32位的</p>
<p>2.使用libsvm for python时出错[1]</p>
<pre><code>Traceback (most recent call last):
File "C:\Python36\lib\libsvm\python\svm.py", line 28, in <module>
libsvm = CDLL(path.join(dirname, r'..\windows\libsvm.dll'))
File "C:\Python36\lib\ctypes\__init__.py", line 348, in __init__
self._handle = _dlopen(self._name, mode)
OSError: [WinError 126] 找不到指定的模块。
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "train.py", line 3, in <module>
from libsvm.python.svmutil import *
File "C:\Python36\lib\libsvm\python\svmutil.py", line 5, in <module>
from svm import *
File "C:\Python36\lib\libsvm\python\svm.py", line 38, in <module>
raise Exception('LIBSVM library not found.')
Exception: LIBSVM library not found.
</code></pre>
<p>出错原因:在github上下载的libsvm中的libsvm.lib是win64版本的,而自己的python版本是32位的<br />
解决方案:<br />
方法1.更换python版本(太麻烦了,还要重新下载好多库)<br />
方法2.去<a href="http://www.lfd.uci.edu/~gohlke/pythonlibs/">万能宝库</a>上下载对应版本的.whl文件,(不用安装)直接解压后找到<a href="https://download.lfd.uci.edu/pythonlibs/o4uhg4xd/libsvm-3.22-cp27-cp27m-win32.whl">libsvm.lib</a>更换即可<br />
2018-09-07 22:11:58</p>
<p>参考资料</p>
<p>1.<a href="https://blog.csdn.net/rena521/article/details/51187981">https://blog.csdn.net/rena521/article/details/51187981</a></p>
位运算的妙用之二进制1的个数
2017-12-26T00:00:00+00:00
2017-12-26T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-wei-yun-suan-de-miao-yong-zhi-er-jin-zhi-1de-ge-shu/
<p>求出一个正整数转换成二进制形式中数字"1"的个数<br />
题目:求出一个正整数转换成二进制形式中数字"1"的个数<br />
如:<br />
int 型数值为 80<br />
转化成二进制形式:80 = 00000000 00000000 00000000 01010000<br />
因此 1 的个数为 2</p>
<p>1.普通解法</p>
<p>一位一位判断</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> int bitCount1(int n) {
int count = 0;
while (n != 0) {
if (n & 0x01 == 1)
count++;
n = n >> 1;
}
return count;
}
</code></pre>
<p>2.大神的解法</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> int bitCount2(int n) {
n = n - ((n >> 1) & 0x55555555);//n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f);
n = n + (n >> 8); //n = (n & 0x00ff00ff) + ((n >> 8) & 0x00ff00ff);
n = n + (n >> 16); //n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);
return n & 0x0000003f; //return n;
}
</code></pre>
<p>参考文章</p>
<p>1.<a href="https://www.jianshu.com/p/25c75149e7a2">https://www.jianshu.com/p/25c75149e7a2</a></p>
C语言文件读写
2017-12-21T00:00:00+00:00
2017-12-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-cyu-yan-wen-jian-du-xie/
<p>C语言文件操作笔记</p>
<pre><code>FILE *fopen(char *filename,char *type); //打开文件
int fclose(FILE *stream); //关闭文件
int fgetc(FILE *stream); //读一个字符指针后移
int getc(FILE *stream);
int fputc(int ch,FILE *stream); //写一个字符指针后移
int putc(int ch,FILE *stream);
</code></pre>
<p>filename指明文件路径<br />
type可取的至如下<br />
"r" :打开一个已有的文本文,允许读取文件<br />
"w"
:打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容<br />
"a"
:打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。<br />
"r+":打开一个文本文件,允许读写文件<br />
"w+":打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件<br />
"a+":打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式<br />
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:<br />
<code>"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"</code></p>
sublime_Text3插入当前时间
2017-12-21T00:00:00+00:00
2017-12-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-sublime-text3cha-ru-dang-qian-shi-jian/
<p>sublime_Text3插入当前时间</p>
<p>1.创建新snippet</p>
<p>tool → new snippet
创建一个新的snippet,并保存为"author.sublime-snippet"(最好在该目录(User)下再创建一个MySnippet目录):<br />
其内容:</p>
<pre><code><snippet>
<content><![CDATA[
/**
* ============================
* @Author: XX
* @Version: 1.0
* @DateTime: ${1:alt+t}
* ============================
*/
]]></content>
<!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
<tabTrigger>author</tabTrigger>
<!-- Optional: Set a scope to limit where the snippet will trigger -->
<!-- <scope>source.python</scope> -->
</snippet>
</code></pre>
<p>2.创建新插件</p>
<p>Tools → New Plugin.
创建时间插件,保存在User目录,命名为addCurrentTime.py:<br />
其内容为:</p>
<pre><code>import sublime, sublime_plugin
import datetime
class AddCurrentTimeCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.run_command("insert_snippet",
{
"contents": "%s" % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
)
</code></pre>
<p>3.绑定快捷键</p>
<p>Preference → Key Bindings → User.绑定快捷键:</p>
<pre><code>[
{
"command": "add_current_time",
"keys": [
"alt+t"
]
}
]
</code></pre>
<p>其中绑定的命令的名字是由所创建的插件的类名而来<br />
AddCurrentTimeCommand--->add_current_time<br />
这应该是Sublime创建插件的规则</p>
<p>参考资料</p>
<p>1,<a href="http://www.cnblogs.com/xiaomingzaixian/p/6984664.html">http://www.cnblogs.com/xiaomingzaixian/p/6984664.html</a><br />
2,<a href="http://blog.csdn.net/sshfl_csdn/article/details/46415551">http://blog.csdn.net/sshfl_csdn/article/details/46415551</a></p>
字符串匹配
2017-12-21T00:00:00+00:00
2017-12-21T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-zi-fu-chuan-pi-pei/
<h1 id="1-bm">1.BM</h1>
<p>从左到右依次比较<br />
s:主串<br />
r:模式串</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> int BM(char *s,int slen,char *r,int rlen){
int i=1,j=1;
while(i<=slen-rlen+1){
while(j<=rlen&&s[i]==r[j]){
i++;j++;
}
if(j>rlen){
return i-j+1;
}else{
j=1;i++;
}
}
return 0;
}
</code></pre>
<h1 id="2-kmpsuan-fa">2.KMP算法</h1>
<p>通过一个next数字<br />
当每次发生不匹配的时候<br />
模式串不必回到开头</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> int* getnext(char *r, int n) { //next数组求解
int i, j;
int *next = (int *)malloc((n + 1) * sizeof(int));
next[0] = n; next[1] = 0;
j = 0;
i=1;
while(i<n){
if (j == 0 || r[i] == r[j]) {
++i; ++j;
next[i] = j;
} else {
j = next[j];
}
}
return next;
}
int KMP(char *s,int slen,char *r,int rlen){
int *next=getnext(r,rlen);
int i=1,j=1;
while(i<=slen&&j<=rlen){
while(j<=rlen&&s[i]==r[j]){
i++;j++;
}
if(j>rlen){
return i-j+1;
}else{
j=next[j];i++;
}
}
return 0;
}
</code></pre>
<h1 id="can-kao-wen-zhang">参考文章</h1>
<p>1.<a href="http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html">字符串匹配的KMP算法</a></p>
图片头文件解析
2017-12-12T00:00:00+00:00
2017-12-12T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-tu-pian-tou-wen-jian-jie-xi/
<p>各种类型的图片头文件解析</p>
<h3 id="1-pngtu-pian-wen-jian-tou">1.PNG图片文件头</h3>
<h4 id="1-1pngwen-jian-jie-gou">1.1PNG文件结构</h4>
<p>PNG图像格式文件由一个8字节的PNG文件标识(file
signature)域和3个以上的后续数据块(IHDR、IDAT、IEND等)组成。<br />
PNG文件包括8字节文件署名(89 50 4E 47 0D 0A 1A
0A,十六进制),用来识别PNG格式。</p>
<table><thead><tr><th style="text-align: left">十六进制</th><th style="text-align: left">含义</th></tr></thead><tbody>
<tr><td style="text-align: left">89</td><td style="text-align: left">用于检测传输系统是否支持8位的字符编码(8 bit data),用以减少将文本文件被错误的识别成PNG文件的机会,反之亦然。</td></tr>
<tr><td style="text-align: left">50 4E 47</td><td style="text-align: left">PNG每个字母对应的ASCII,让用户可以使用文本编辑器查看时,识别出是PNG文件。</td></tr>
<tr><td style="text-align: left">0D 0A</td><td style="text-align: left">DOS风格的换行符(CRLF)。用于DOS-Unix数据的换行符转换。</td></tr>
<tr><td style="text-align: left">1A</td><td style="text-align: left">在DOS命令行下,用于阻止文件显示的文件结束符。</td></tr>
<tr><td style="text-align: left">0A</td><td style="text-align: left">Unix风格的换行符(LF)。用于Unix-DOS换行符的转换。</td></tr>
</tbody></table>
<h3 id="1-2pngshu-ju-kuai-chunk">1.2PNG数据块(chunk)</h3>
<p>PNG定义了两种类型的数据块,一种是称为关键数据块(critical
chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary
chunks),这是可选的数据块。<br />
关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。虽然PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。<br />
下表就是PNG中数据块的类别,其中,关键数据块部分我们使用红色背景加以区分。</p>
<p>数据块符号 数据块名称 多数据块 可选否 位置限制</p>
<hr />
<pre><code> IHDR 文件头数据块 否 否 第一块
cHRM 基色和白色点数据块 否 是 在PLTE和IDAT之前
gAMA 图像γ数据块 否 是 在PLTE和IDAT之前
sBIT 样本有效位数据块 否 是 在PLTE和IDAT之前
PLTE 调色板数据块 否 是 在IDAT之前
bKGD 背景颜色数据块 否 是 在PLTE之后IDAT之前
hIST 图像直方图数据块 否 是 在PLTE之后IDAT之前
tRNS 图像透明数据块 否 是 在PLTE之后IDAT之前
oFFs (专用公共数据块) 否 是 在IDAT之前
pHYs 物理像素尺寸数据块 否 是 在IDAT之前
sCAL (专用公共数据块) 否 是 在IDAT之前
IDAT 图像数据块 是 否 与其他IDAT连续
tIME 图像最后修改时间数据块 否 是 无限制
tEXt 文本信息数据块 是 是 无限制
zTXt 压缩文本数据块 是 是 无限制
fRAc (专用公共数据块) 是 是 无限制
gIFg (专用公共数据块) 是 是 无限制
gIFt (专用公共数据块) 是 是 无限制
gIFx (专用公共数据块) 是 是 无限制
IEND 图像结束数据 否 否 最后一个数据块
</code></pre>
CentOS自带python26升级到27
2017-09-12T00:00:00+00:00
2017-09-12T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-centoszi-dai-python26sheng-ji-dao-27/
<p>1.下载安装</p>
<pre><code>#下载
wget http://python.org/ftp/python/2.7.3/Python-2.7.3.tar.bz2
#解压并更改目录
tar -jxvf Python-2.7.3.tar.bz2
cd Python-2.7.3
#编译安装
./configure
make all
make install
make clean
make distclean
</code></pre>
<p>安装需要gcc(如果系统没有gcc需要自己安装)</p>
<pre><code>yum install -y gcc
</code></pre>
<p>查看版本信息</p>
<pre><code>[root@host ~]# /usr/local/bin/python2.7 -V
Python 2.7.3
[root@host ~]# python -V
Python 2.6.6
</code></pre>
<p>python2.7已经安装,但系统的python还是2.6</p>
<p>2.建立软连接</p>
<p>使系统默认的python指向python2.7</p>
<pre><code>rm /usr/bin/python /usr/bin/python2.6
ln -s /usr/local/bin/python2.7 /usr/bin/python
</code></pre>
<p>但yum命令依赖python2.6.6,需要更改一些配置文件</p>
<pre><code>su root ##切换到root
vi /usr/bin/yum ##打开文件
</code></pre>
<p><del>/usr/bin/python</del> ##删除此行<br />
/usr/bin/python2.6.6 ##改为此行<br />
更改需要管理员权限</p>
<p>参考资料</p>
<p>1.<a href="http://blog.csdn.net/jcjc918/article/details/11022345">http://blog.csdn.net/jcjc918/article/details/11022345</a></p>
C实现简单TCP通信
2017-09-05T00:00:00+00:00
2017-09-05T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-cshi-xian-jian-dan-tcptong-xin/
<p>C实现简单TCP通信</p>
<h3 id="1-tian-jia-suo-xu-de-windowku">1.添加所需的window库</h3>
<p>在VC6++下编程<br />
代码用到了其他的lib库,需要添加<br />
工程--->设置--->Link--->library
modules添加ws2_32.lib(与其他lib用空格隔开)</p>
<h3 id="2-fu-wu-qi-duan-dai-ma">2.服务器端代码</h3>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> #include"winsock2.h"
#include "stdio.h"
#define SERVER_PORP 8884//服务器端口号
void main()
{
//加载套接字(winsock)库,
//加载这段代码拷贝于MSDN中WSAStartup的介绍
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
//版本号为1.1
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
//创建套接字
SOCKET sockServer=socket(AF_INET,SOCK_STREAM,0);
//AF_INET指协议簇
//SOCK_STREAM参数设置为TCP连接
SOCKADDR_IN addrServer;
//设置服务器端套接字的相关属性
addrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
//设置IP
addrServer.sin_family=AF_INET;
addrServer.sin_port=htons(SERVER_PORP);
//设置端口号
//将套接字绑定到本地地址和指定端口上
bind(sockServer,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
//将套接字设置为监听模式,并将最大请求连接数设置成5,超过此数的请求全部作废
listen(sockServer,5);
SOCKADDR_IN addrClient; //用来接收客户端的设置,包括IP和端口
int len=sizeof(SOCKADDR);
while(1) //不断监听
{
//得到创建连接后的一个新的套接字,用来和客户端进行沟通,原套接字继续监听客户的连接请求
SOCKET sockConn=accept(sockServer,(SOCKADDR*)&addrClient,&len);
if(sockConn!=INVALID_SOCKET) //创建成功
{
char sendInfo[100];
//inet_ntoa将结构转换为十进制的IP地址字符串
sprintf(sendInfo,"welcome %s to this Server!",inet_ntoa(addrClient.sin_addr));
//成功建立连接后向客户端发送数据,结果将显示在客户端上
send(sockConn,sendInfo,strlen(sendInfo)+1,0);
//从客户端接收数据,结果显示在服务器上
char recvInfo[100];
recv(sockConn,recvInfo,100,0);
printf("%s\n",recvInfo);
while(1)
{
if(recv(sockConn,recvInfo,100,0)<0)
break; //客户端断开recv返回负值
printf("%s\n",recvInfo);
sprintf(sendInfo,recvInfo);
send(sockConn,sendInfo,strlen(sendInfo)+1,0);
sprintf(sendInfo,"\nsever receive this text!\n");
send(sockConn,sendInfo,strlen(sendInfo)+1,0);
}
//将本次建立连接中得到套接字关闭
closesocket(sockConn);
}
else
{
int errCode=WSAGetLastError();
printf("the errcode is:%d\n",errCode);
}
}
//如果本程序不是死循环,那么在此处还应添加以下代码:
closesocket(sockServer); //对一直处于监听状态的套接字进行关闭
WSACleanup(); //终止对winsocket库的使用
}
</code></pre>
<h3 id="3-ke-hu-duan-dai-ma">3.客户端代码</h3>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> #include "winsock2.h"
#include "stdio.h"
#define SERVER_IP "127.0.0.1" //服务器IP地址
#define SERVER_PORP 8884 //服务器端口号
void main()
{
//加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 ); //版本好为1.1
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0); //SOCK_STREAM参数设置为TCP连接
SOCKADDR_IN addrServer; //服务器地址结构
addrServer.sin_addr.S_un.S_addr=inet_addr(SERVER_IP); //服务器地址
addrServer.sin_port=htons(SERVER_PORP); //服务器端口号
addrServer.sin_family=AF_INET;
//与服务器端建立连接,进行通信
int connReult=connect(sockClient,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
if(connReult!=WSAEADDRNOTAVAIL) //访问成功
{
//成功建立连接后向服务器端发送数据,结果将显示在服务器端上
char sendInfo[100];
send(sockClient,"this is zhangsan!",strlen("this is zhangsan!")+1,0);
//接收来自服务器端发送来的信息
char recvInfo[100];
recv(sockClient,recvInfo,100,0);
printf("%s\n",recvInfo);
while(1)
{
scanf("%s",sendInfo);
send(sockClient,sendInfo,strlen(sendInfo)+1,0);
recv(sockClient,recvInfo,100,0);
printf("%s",recvInfo);
recv(sockClient,recvInfo,100,0);
printf("%s",recvInfo);
}
}
else
{
int errCode=WSAGetLastError();
printf("the errcode is:%d\n",errCode);
}
closesocket(sockClient);
WSACleanup();
}
</code></pre>
<p>客户端和服务器应分别属于两个exe<br />
先运行服务器,后运行客户端</p>
VC6聊天室程序设计
2017-09-05T00:00:00+00:00
2017-09-05T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-vc6liao-tian-shi-cheng-xu-she-ji/
<p>了解了C语言多线程的实例和简单的TCP通信,来编写一个简单的聊天室<br />
曾经java实现的聊天室程序在这里</p>
<h3 id="1-ke-hu-duan-cheng-xu">1.客户端程序</h3>
<p>客户端需要两个线程,主线程接受用户输入并发送到服务器<br />
另一个线程监听服务器发来的消息,显示在屏幕上</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> #include "winsock2.h"
#include "stdio.h"
#define SERVER_IP "10.80.167.248"
#define SERVER_PORP 8884
//blog:zfblog.xyz
//author:Frey
DWORD WINAPI ThreadFun(LPVOID pM)
{
SOCKET sockClient=*(SOCKET *)pM;
char recvInfo[100];
while(1)
{
if(recv(sockClient,recvInfo,100,0)>0)
{
printf("%s",recvInfo);
}
}
return 0;
}
void main()
{
//加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 ); //版本好为1.1
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0); //SOCK_STREAM参数设置为TCP连接
SOCKADDR_IN addrServer; //服务器地址结构
addrServer.sin_addr.S_un.S_addr=inet_addr(SERVER_IP); //服务器地址
addrServer.sin_port=htons(SERVER_PORP); //服务器端口号
addrServer.sin_family=AF_INET;
//与服务器端建立连接,进行通信
char name[100];
printf("请输入姓名:");
scanf("%s",name);
int connReult=connect(sockClient,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
if(connReult!=WSAEADDRNOTAVAIL) //访问成功
{
CreateThread(NULL, 0, ThreadFun, (void *)&sockClient, 0, NULL);
printf("连接成功\n");
//成功建立连接后向服务器端发送数据,结果将显示在服务器端上
char sendInfo[100];
sprintf(sendInfo,name);
send(sockClient,sendInfo,strlen(sendInfo)+1,0);
//接收来自服务器端发送来的信息
//char recvInfo[100];
//recv(sockClient,recvInfo,100,0);
//printf("%s\n",recvInfo);
while(1)
{
scanf("%s",sendInfo);
send(sockClient,sendInfo,strlen(sendInfo)+1,0);
}
}
else
{
int errCode=WSAGetLastError();
printf("the errcode is:%d\n",errCode);
}
closesocket(sockClient);
WSACleanup();
}
</code></pre>
<h3 id="2-fu-wu-qi-cheng-xu">2.服务器程序</h3>
<p>用一个链表来存储用户的套接字和姓名<br />
主线程监听用户的连接,每当有新用户连接,将其信息加入链表,并传入第二个线程<br />
第二个线程接受客户端发来的信息,并将信息传入第三个线程<br />
第三个线程从链表中读取用户套接字,将接到的信息转发给所有在线用户</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> #include "winsock2.h"
#include "stdio.h"
#include <windows.h>
//blog:zfblog.xyz
//author:Frey
#define SERVER_PORP 8884
struct client_info{
SOCKET sockConn;
char name[100];
client_info *next;
};//存放每一个用户的信息
client_info * C_info_head;
DWORD WINAPI Threadmes(LPVOID pM)
{
char sendInfo[100];
sprintf(sendInfo,(char *)pM);
for(int i=0;i<=c_info_num;i++){
SOCKET sockConn=C_info[i].sockConn;
//if(C_info[i].num!=-1){
send(sockConn,sendInfo,strlen(sendInfo)+1,0);
//}
}
return 0;
}
//将某条消息群发给所有客户端
void sendmessage(char *message)
{
CreateThread(NULL, 0, Threadmes, (void *)message, 0, NULL);
}
//接收每个用户的信息
DWORD WINAPI ThreadFun(LPVOID pM)
{
client_info c_info = *(client_info *)pM;
SOCKET sockConn=c_info.sockConn;
char sendInfo[100];
//inet_ntoa将结构转换为十进制的IP地址字符串
//sprintf(sendInfo,"welcome %s to this Server!",inet_ntoa(addrClient.sin_addr));
//成功建立连接后向客户端发送数据,结果将显示在客户端上
//send(sockConn,sendInfo,strlen(sendInfo)+1,0);
//从客户端接收数据,结果显示在服务器上
char recvInfo[100];
recv(sockConn,recvInfo,100,0);
sprintf(c_info.name,recvInfo);
printf("欢迎%s进入聊天室\n",recvInfo);
sprintf(sendInfo,"欢迎%s进入聊天室\n",recvInfo);
sendmessage(sendInfo);
while(1)
{
if(recv(sockConn,recvInfo,100,0)<0)
break;
printf("[%s]:%s\n",c_info.name,recvInfo);
sprintf(sendInfo,"[%s]:%s\n",c_info.name,recvInfo);
sendmessage(sendInfo);
}
//将本次建立连接中得到套接字关闭
closesocket(sockConn);
return 0;
}
void main()
{
//加载套接字(winsock)库,加载这段代码拷贝于MSDN中WSAStartup的介绍
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 ); //版本号为1.1
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
//消息存储及发送
//----------------------------------------------------
//创建套接字
SOCKET sockServer=socket(AF_INET,SOCK_STREAM,0); //SOCK_STREAM参数设置为TCP连接
SOCKADDR_IN addrServer; //设置服务器端套接字的相关属性
addrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY); //设置IP
addrServer.sin_family=AF_INET;
addrServer.sin_port=htons(SERVER_PORP); //设置端口号
//将套接字绑定到本地地址和指定端口上
bind(sockServer,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
//将套接字设置为监听模式,并将最大请求连接数设置成5,超过此数的请求全部作废
listen(sockServer,5);
SOCKADDR_IN addrClient; //用来接收客户端的设置,包括IP和端口
int len=sizeof(SOCKADDR);
while(1) //不断监听
{
//得到创建连接后的一个新的套接字,用来和客户端进行沟通,原套接字继续监听客户的连接请求
SOCKET sockConn=accept(sockServer,(SOCKADDR*)&addrClient,&len);
if(sockConn!=INVALID_SOCKET) //创建成功
{
c_info_num++;
C_info[c_info_num].num=c_info_num;
C_info[c_info_num].sockConn=sockConn;
CreateThread(NULL, 0, ThreadFun, (void *)&C_info[c_info_num], 0, NULL);
}
else
{
int errCode=WSAGetLastError();
printf("the errcode is:%d\n",errCode);
}
}
//如果本程序不是死循环,那么在此处还应添加以下代码:
closesocket(sockServer); //对一直处于监听状态的套接字进行关闭
WSACleanup(); //终止对winsocket库的使用
}
</code></pre>
<p>未完成的问题<br />
用户离线时出现bug<br />
全部代码:<a href="https://github.com/summerIwinter/Chatroom">https://github.com/summerIwinter/Chatroom</a></p>
C语言多线程学习
2017-09-04T00:00:00+00:00
2017-09-04T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-cyu-yan-duo-xian-cheng-xue-xi/
<p>VC6++环境下C语言创建多线程</p>
<h3 id="1-jian-dan-de-chuang-jian-duo-xian-cheng-shi-li">1.简单的创建多线程实例</h3>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> #include <stdio.h>
#include <windows.h>
//子线程函数
DWORD WINAPI ThreadFun(LPVOID pM){
printf("子线程的线程ID号为:%d\n子线程输出 Hello World\n", GetCurrentThreadId());
return 0;
}
//主函数,所谓主函数其实就是主线程执行的函数。
int main()
{
printf("最简单的创建多线程实例\n");
printf("http://zfblog.xyz");
HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);
WaitForSingleObject(handle, INFINITE);
return 0;
}
</code></pre>
<p>使用CreateThread函数创建线程</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> HANDLE WINAPI CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId);
</code></pre>
<p>第一个参数表示线程内核对象的安全属性,一般传入NULL表示使用默认设置<br />
第二个参数表示线程栈空间大小,传入0表示使用默认大小(1MB)<br />
第三个参数表示新线程所执行的线程函数地址<br />
多个线程可以使用同一个函数地址(线程函数入口)<br />
第四个参数是传给线程函数的参数<br />
第五个参数指定额外的标志来控制线程的创建<br />
就是控制线程何时运行<br />
为0:直接运行<br />
为CREATE_SUSPENDED:调用ResumeThread()后运行<br />
第六个参数将返回线程的ID号,传入NULL表示不需要返回该线程ID号<br />
函数返回值:成功返回新线程的句柄,失败返回NULL</p>
<h3 id="2-guan-yu-createthread-gei-xian-cheng-han-shu-chuan-di-can-shu-de-wen-ti">2.关于CreateThread()给线程函数传递参数的问题</h3>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> //创建线程时传递参数
int *parameter;
CreateThread(NULL, 0, ThreadFun, (void *)parameter, 0, NULL);
//想到一个问题直接使用全局的static变量会出现什么情况?????
//parameter是任意类型的地址
//线程函数中调用参数
DWORD WINAPI ThreadFun(LPVOID pM)
{
//传入的地址指向一个int类型的数据
int p=*PM;
return 0;
}
</code></pre>
<p>WaitForSingleObject函数作用</p>
<pre data-lang="c" class="language-c "><code class="language-c" data-lang="c"> DWORD WINAPI WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
</code></pre>
<p>第一个参数为要等待的内核对象<br />
第二个参数为最长等待的时间<br />
为5000:等待5秒<br />
为0:立即执行<br />
为INFINITE:无限等待<br />
函数返回值:WAIT_OBJECT_0在指定的时间内对象被触发<br />
WAIT_TIMEOUT超过最长等待时间对象仍未被触发<br />
WAIT_FAILED传入参数有错误<br />
函数作用<br />
首先要知道----线程的句柄在线程运行时是未触发的,结束后触发<br />
在主函数中调用等待线程执行完毕,然后主函数结束<br />
不用此函数可能会导致线程没有执行完,主函数结束,程序结束</p>
windows系统盘瘦身
2017-09-02T00:00:00+00:00
2017-09-02T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-windowsxi-tong-pan-shou-shen/
<p>C盘满了,瘦一下身</p>
<h3 id="1-guan-bi-xi-tong-xiu-mian-gong-neng">1.关闭系统休眠功能</h3>
<p>删除Hiberfil.sys文件<br />
管理员运行cmd输入下面命令<br />
<code>powercfg -h of</code></p>
<p>2.设置虚拟内存</p>
<p>控制面板--->(系统和安全)--->系统--->高级系统设置--->(高级)--->设置--->(高级)--->更改<br />
把虚拟内存设置到其它盘</p>
<h3 id="3-shan-chu-lin-shi-wen-jian">3.删除临时文件</h3>
<p>以下三个目录里的文件是临时文件,可以删除<br />
C:\windows\temp<br />
C:\ProgramData\TEMP<br />
C:\Users\你的用户名\AppData\Local\Temp</p>
powershell脚本遍历目录
2017-09-01T00:00:00+00:00
2017-09-01T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-powershelljiao-ben-bian-li-mu-lu/
<p>powershell脚本遍历目录</p>
<h3 id="1-bian-li-mu-lu-ji-zi-mu-lu">1.遍历目录及子目录</h3>
<pre><code>function filesize ([string]$filepath) {
if ($filepath -eq $null) {
throw "路径不能为空"
}
dir -Path $filepath | ForEach-Object -Process {
if ($_.psiscontainer -eq $true) {
$length = 0
dir -Path $_.fullname -Recurse | ForEach-Object {
$length += $_.Length
}
$l = $length / 1KB
$_.name + "文件夹的大小为: {0:n1} KB" -f $l
}
}
}
filesize -filepath "D:\"
</code></pre>
<h3 id="2-yun-xing-shi-li">2.运行示例</h3>
<pre><code>PS F:\python> function filesize ([string]$filepath) {
>> if ($filepath -eq $null) {
>> throw "路径不能为空"
>> }
>> dir -Path $filepath | ForEach-Object -Process {
>> if ($_.psiscontainer -eq $true) {
>> $length = 0
>> dir -Path $_.fullname -Recurse | ForEach-Object {
>> $length += $_.Length
>> }
>> $l = $length / 1KB
>> $_.name + "文件夹的大小为: {0:n1} KB" -f $l
>> }
>> }
>> }
PS F:\python> filesize -filepath "f:\python"
ipluyou文件夹的大小为: 82,196.5 KB
loginip_jlu文件夹的大小为: 16.8 KB
嗅事百科文件夹的大小为: 4.3 KB
小说文件夹的大小为: 31,043.6 KB
笑料文件夹的大小为: 1,578,575.8 KB
PS F:\python>
</code></pre>
powershell卸载自带应用
2017-09-01T00:00:00+00:00
2017-09-01T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-powershellxie-zai-zi-dai-ying-yong/
<p>通过powershell卸载windows自带应用</p>
<h3 id="1-xie-zai-quan-bu-zi-dai-ying-yong">1.卸载全部自带应用</h3>
<p><code>Get-AppxPackage -User $env:USERNAME | Remove-AppxPackage</code></p>
<h3 id="2-qi-ta-geng-duo-xie-zai">2.其他更多卸载</h3>
<p>OneNote:<br />
<code>Get-AppxPackage OneNote | Remove-AppxPackage</code><br />
3D:<br />
<code>Get-AppxPackage 3d | Remove-AppxPackage</code><br />
Camera相机:<br />
<code>Get-AppxPackage camera | Remove-AppxPackage</code><br />
邮件和日历:<br />
<code>Get-AppxPackage communi | Remove-AppxPackage</code><br />
新闻订阅:<br />
<code>Get-AppxPackage bing | Remove-AppxPackage</code><br />
Groove音乐电影与电视:<br />
<code>Get-AppxPackage zune | Remove-AppxPackage</code><br />
人脉:<br />
<code>Get-AppxPackage people | Remove-AppxPackage</code><br />
手机伴侣Phone Companion:<br />
<code>Get-AppxPackage phone | Remove-AppxPackage</code><br />
照片:<br />
<code>Get-AppxPackage photo | Remove-AppxPackage</code><br />
纸牌游戏(还敢要钱的那货):<br />
<code>Get-AppxPackage solit | Remove-AppxPackage</code><br />
录音机:<br />
<code>Get-AppxPackage soundrec | Remove-AppxPackage</code><br />
Xbox:<br />
<code>Get-AppxPackage xbox | Remove-AppxPackage</code></p>
自己写一个Android照相机应用-2
2017-08-19T00:00:00+00:00
2017-08-19T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-zi-ji-xie-yi-ge-androidzhao-xiang-ji-ying-yong-2/
<p>Android自定义相机的实现<br />
首先是要创建camera的生命周期</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java"> getCamera();//获取Camera对象
setStartPreview(Camera camera,SurfaceHolder holder);//预览相机内容
releaseCamera();//释放相机资源
activity生命周期与camera绑定
protected void onResume() {
super.onResume();
if (mCamera == null) {
mCamera = getCamera();
if (mHolder != null) {
setStartPreview(mCamera,mHolder);
}
}
}
protected void onPause() {
super.onPause();
releaseCamera();
}
</code></pre>
<p>camera与surfaceview绑定</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java"> //预览图像与camera绑定
@Override
public void surfaceCreated(SurfaceHolder holder) {
setStartPreview(mCamera,mHolder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mCamera.stopPreview();
setStartPreview(mCamera,mHolder);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
releaseCamera();
}
</code></pre>
<p>声明相机使用权限</p>
<pre data-lang="xml" class="language-xml "><code class="language-xml" data-lang="xml"> <uses-permission android:name="android.permission.CAMERA"</uses-permission>
</code></pre>
<p>实现照相,通过回调将照片数据保存到文件,并将文件路径传递到其它activity(ResultAty)</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java"> private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {//data中存储照片的全部信息
File tempFile = new File("/sdacrd/temp.png");
try {
FileOutputStream fos = new FileOutputStream(tempFile);
fos.write(data);
fos.close();
Intent intent = new Intent(CustomCamera.this,ResultAty.class);
intent.putExtra("picPath",tempFile.getAbsolutePath());
startActivity(intent);
CustomCamera.this.finish();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
public void capture(View view){
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPictureFormat(ImageFormat.JPEG);//设置拍照格式jpg格式
parameters.setPictureSize(800,400);//设置照片大小
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);//设置自动对焦
mCamera.autoFocus(new Camera.AutoFocusCallback() { //回调,对焦最清晰时拍照
@Override
public void onAutoFocus(boolean success, Camera camera) {
if (success) {
mCamera.takePicture(null,null,mPictureCallback);
}
}
});
}
</code></pre>
JAVA套接字之TCP实现聊天室
2017-08-10T00:00:00+00:00
2017-08-10T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-javatao-jie-zi-zhi-tcpshi-xian-liao-tian-shi/
<p>java实现聊天室,通过多线程实现随时加入,随时退出</p>
<h3 id="1-ke-hu-duan-cheng-xu">1.客户端程序</h3>
<p>客户端有两个线程<br />
一个线程由主类SocketClient实现向服务器发送消息<br />
一个线程由内部类readLineThread实现监听服务器发来的消息并显示</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java"> import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class SocketClient extends Socket{
private static final String SERVER_IP = "127.0.0.1";
private static final int SERVER_PORT = 2017;
private Socket client;
private PrintWriter out;
private BufferedReader in;
/**
* 与服务器连接,并输入发送消息
*/
public SocketClient() throws Exception{
super(SERVER_IP, SERVER_PORT);
client = this;
out = new PrintWriter(this.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(this.getInputStream()));
new readLineThread();
while(true){
in = new BufferedReader(new InputStreamReader(System.in));
String input = in.readLine();
out.println(input);
}
}
/**
* 用于监听服务器端向客户端发送消息线程类
*/
class readLineThread extends Thread{
private BufferedReader buff;
public readLineThread(){
try {
buff = new BufferedReader(new InputStreamReader(client.getInputStream()));
start();
} catch (Exception e) {
}
}
@Override
public void run() {
try {
while(true){
String result = buff.readLine();
if("byeClient".equals(result)){//客户端申请退出,服务端返回确认退出
break;
}else{//输出服务端发送消息
System.out.println(result);
}
}
in.close();
out.close();
client.close();
} catch (Exception e) {
}
}
}
public static void main(String[] args) {
try {
new SocketClient();//启动客户端
} catch (Exception e) {
}
}
}
</code></pre>
<h3 id="2-fu-wu-qi-cheng-xu">2.服务器程序</h3>
<p>服务器由三个类实现<br />
主类Server监听客户端请求,并启用线程处理请求<br />
内部类PrintOutThread监听输出消息请求,将消息发送到所有客户端<br />
内部类ServerThread提供与每一个用户的连接</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java"> import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Server extends ServerSocket {
private static final int SERVER_PORT = 2017;
private static boolean isPrint = false;// 是否输出消息标志
private static List user_list = new ArrayList();// 登录用户集合
private static List<ServerThread> thread_list = new ArrayList<ServerThread>();// 服务器已启用线程集合
private static LinkedList message_list = new LinkedList();// 存放消息队列
/**
* 创建服务端Socket,创建向客户端发送消息线程,监听客户端请求并处理
*/
public Server() throws IOException {
super(SERVER_PORT);// 创建ServerSocket
new PrintOutThread();// 创建向客户端发送消息线程
try {
while (true) {// 监听客户端请求,启用一个线程处理
Socket socket = accept();
new ServerThread(socket);
}
} catch (Exception e) {
} finally {
close();
}
}
/**
* 监听是否有输出消息请求线程类,向客户端发送消息
*/
class PrintOutThread extends Thread {
public PrintOutThread() {
start();
}
@Override
public void run() {
while (true) {
//没有打印这句,if里面的语句不会执行,可能是多线程访问isPrint造成的
System.out.println("运行中。。。"+isPrint);
if (isPrint) {// 将缓存在队列中的消息按顺序发送到各客户端,并从队列中清除。
String message = (String) message_list.getFirst();
for (ServerThread thread : thread_list) {
thread.sendMessage(message);
}
message_list.removeFirst();
isPrint = message_list.size() > 0 ? true : false;
}
}
}
}
/**
* 服务器线程类
*/
@SuppressWarnings("unchecked")
class ServerThread extends Thread {
private Socket client;
private PrintWriter out;
private BufferedReader in;
private String name;
public ServerThread(Socket s) throws IOException {
client = s;
out = new PrintWriter(client.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
//in.readLine();
out.println("成功连上聊天室,请输入你的名字:");
start();
}
@Override
public void run() {
try {
int flag = 0;
String line = in.readLine();
while (true) {
// 查看在线用户列表
if ("showuser".equals(line)) {
out.println(this.listOnlineUsers());
}
if("bye".equals(line)){
out.println("bye");
break;}
// 第一次进入,保存名字
if (flag++ == 0) {
name = line;
user_list.add(name);
thread_list.add(this);
out.println(name + "你好,可以开始聊天了...");
this.pushMessage("Client<" + name + ">进入聊天室...");
} else {
this.pushMessage("Client<" + name + "> say : " + line);
}
line = in.readLine();
}
out.println("byeClient");
} catch (Exception e) {
e.printStackTrace();
} finally {// 用户退出聊天室
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
thread_list.remove(this);
user_list.remove(name);
pushMessage("Client<" + name + ">退出了聊天室");
}
}
// 放入消息队列末尾,准备发送给客户端
private void pushMessage(String msg) {
message_list.addLast(msg);
isPrint = true;
}
// 向客户端发送一条消息
private void sendMessage(String msg) {
out.println(msg);
}
// 统计在线用户列表
private String listOnlineUsers() {
String s = "--- 在线用户列表 ---\015\012";
for (int i = 0; i < user_list.size(); i++) {
s += "[" + user_list.get(i) + "]\015\012";
}
s += "--------------------";
return s;
}
}
public static void main(String[] args) throws IOException {
new Server();// 启动服务端
}
}
</code></pre>
<p>这里好像出现了多线程问题</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java"> @Override
public void run() {
while (true) {
//没有打印下面这句,if里面的语句不会执行,可能是这个线程一直访问isPrint,改变它的线程不能访问到它造成的
System.out.println("运行中。。。"+isPrint);
if (isPrint) {// 将缓存在队列中的消息按顺序发送到各客户端,并从队列中清除。
String message = (String) message_list.getFirst();
for (ServerThread thread : thread_list) {
thread.sendMessage(message);
}
message_list.removeFirst();
isPrint = message_list.size() > 0 ? true : false;
}
}
}
</code></pre>
JAVA套接字之TCP简单实用
2017-08-09T00:00:00+00:00
2017-08-09T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-javatao-jie-zi-zhi-tcpjian-dan-shi-yong/
<p>java套接字之TCP</p>
<h3 id="1-dan-ke-hu-duan-pai-dui-yu-fu-wu-qi-jian-li-lian-jie">1.单客户端排队与服务器建立连接</h3>
<p>客户端程序</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java"> import java.net.*;
import java.io.*;
public class Client{
static Socket server;
public static void main(String[] args)throws Exception{
//客户端请求与本机在5678端口建立TCP连接
server=new Socket(InetAddress.getLocalHost(),5678);
BufferedReader in=
new BufferedReader(new InputStreamReader(server.getInputStream()));
//获取Socket的输入流,用来接收从服务端发送过来的数据
PrintWriter out=new PrintWriter(server.getOutputStream());
//获取Socket的输出流,用来发送数据到服务端
BufferedReader wt=new BufferedReader(new InputStreamReader(System.in));
//从键盘输入的数据流
while(true){
String str=wt.readLine();//读取键盘输入字符串
out.println(str);//发送数据到服务端
out.flush();
if(str.equals("end")){
break;
}
System.out.println(in.readLine());//打印服务器返回的字符串
}
server.close();//关闭连接
}
}
</code></pre>
<p>服务器程序1</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java"> import java.io.*;
import java.net.*;
public class Server {
public static void main(String[] args) throws IOException{
ServerSocket server=new ServerSocket(5678);//通信端口
while(true){//一个客户端断开连接后,等待与另一个客户端建立连接
Socket client=server.accept();//accept用于产生"阻塞",直到接受到一个连接,并且返回一个客户端的Socket对象实例。
BufferedReader in=
new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out=new PrintWriter(client.getOutputStream());
while(true){//直到客户端发送end退出循环
String str=in.readLine();
System.out.println(str);
out.println("has receive....");
out.flush();
if(str.equals("end"))
break;
}
client.close();//关闭通信
}
}
}
</code></pre>
<h3 id="2-duo-ge-ke-hu-duan-tong-shi-yu-fu-wu-qi-jian-li-lian-jie">2.多个客户端同时与服务器建立连接</h3>
<p>客户端程序不变,服务器端阻塞等待客户端连接,并给每个链接分配一个线程</p>
<p>服务器程序2</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java"> import java.io.*;
import java.net.*;
public class Server extends Thread {//继承Thread类
private Socket client;
public Server(Socket c){//类初始化,接受参数为客户端的请求
this.client = c;
}
public void run(){//重写run函数
try{
BufferedReader in=
new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out=new PrintWriter(client.getOutputStream());
while(true){
String str=in.readLine();
System.out.println(str);
out.println("has receive....");
out.flush();
if(str.equals("end"))
break;
}
client.close();
}catch(IOException ex){
}finally{
}
}
public static void main(String[] args) throws IOException{
ServerSocket server=new ServerSocket(5678);
while(true){
Server mu=new Server(server.accept());//每当有客户端请求就新建一个Server类与之通信
mu.start();//启动进程
}
}
}
</code></pre>
Matlab函数freqs和freqz
2017-08-09T00:00:00+00:00
2017-08-09T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-matlabhan-shu-freqshe-freqz/
<p>matlab中的freqs和freqz函数</p>
<h3 id="1-freqs">1.freqs</h3>
<p>模拟滤波器的频率响应<br />
语法:</p>
<pre data-lang="matlab" class="language-matlab "><code class="language-matlab" data-lang="matlab"> h = freqs(b,a,w)
[h,w] = freqs(b,a)
[h,w] = freqs(b,a,f)
freqs(b,a)
</code></pre>
<h4 id="1-1miao-shu">1.1描述</h4>
<p>freqs 返回一个模拟滤波器的H(jw)的复频域响应(拉普拉斯格式)</p>
<p><img src="https://blog.vhcffh.com/2017/hexo-matlabhan-shu-freqshe-freqz/freqs.png" alt="" /></p>
<p>h = freqs(b, a, w) 根据系数向量计算返回模拟滤波器的复频域响应<br />
freqs
计算在复平面虚轴上的频率响应h,角频率w确定了输入的实向量,因此必须包含至少一个频率点。<br />
[h, w] = freqs(b, a) 自动挑选200个频率点来计算频率响应h<br />
[h, w] = freqs(b, a, f) 挑选f个频率点来计算频率响应h</p>
<h4 id="1-2li-zi">1.2例子</h4>
<p>找到并画出下面传递函数的频率响应
<img src="https://blog.vhcffh.com/2017/hexo-matlabhan-shu-freqshe-freqz/example1.png" alt="" /></p>
<p>Matlab代码:</p>
<pre data-lang="matlab" class="language-matlab "><code class="language-matlab" data-lang="matlab"> a = [1 0.4 1];
b = [0.2 0.3 1];
w = logspace(-1, 1);
freqs(b, a, w);
</code></pre>
<p>logspace
功能:生成从10的a次方到10的b次方之间按对数等分的n个元素的行向量<br />
n如果省略,则默认值为50。</p>
<pre data-lang="matlab" class="language-matlab "><code class="language-matlab" data-lang="matlab"> h=freqs(b,a,w);
mag = abs(h);phase = angle(h);
subplot(2,1,1), loglog(w,mag);
subplot(2,1,2), semilogx(w,phase);
f = w/(2*pi);mag = 20*log10(mag);phase = phase*180/pi;
</code></pre>
<p><img src="https://blog.vhcffh.com/2017/hexo-matlabhan-shu-freqshe-freqz/frequency.png" alt="频率响应" /></p>
<h3 id="2-freqz">2.freqz</h3>
<p>MATLAB提供了专门用于求离散系统频响特性的函数freqz()<br />
<img src="https://blog.vhcffh.com/2017/hexo-matlabhan-shu-freqshe-freqz/freqz.png" alt="" />
调用freqz()的格式有以下两种:</p>
<h4 id="2-1-h-w-freqz-b-a-n">2.1[H,w]=freqz(B,A,N)</h4>
<p>B和A分别为离散系统的系统函数分子、分母多项式的系数向量,N为正整数,返回量H则包含了离散系统频响
在
0------pi范围内N个频率等分点的值,向量w则包含范围内N个频率等分点。调用中若N默认,默认值为512。</p>
<h4 id="2-2-h-w-freqz-b-a-n-whole">2.2[H,w]=freqz(B,A,N,’whole’)</h4>
<p>该调用格式将计算离散系统在0---pi范内的N个频率等分店的频率响应的值。因此,可以先调用freqz()函数计算系统的频率响应,然后利用abs()和angle()函数及plot()函数,即可绘制出系统在
或 范围内的频响曲线。<br />
例:绘制如下系统的频响曲线<br />
H(z)=(z-0.5)/z<br />
MATLAB命令如下:</p>
<pre><code>B=[1 -0.5];
A =[1 0];
[H,w]=freqz(B,A,400,'whole');
</code></pre>
<p>H是频率响应的幅度,w是0---pi内的400个点</p>
<pre><code>Hf=abs(H);
Hx=angle(H);
clf
figure(1)
plot(w,Hf)
title('离散系统幅频特性曲线')
figure(2)
plot(w,Hx)
title('离散系统相频特性曲线')
</code></pre>
<p><img src="https://blog.vhcffh.com/2017/hexo-matlabhan-shu-freqshe-freqz/amp.png" alt="" />
<img src="https://blog.vhcffh.com/2017/hexo-matlabhan-shu-freqshe-freqz/ang.png" alt="" /></p>
<p>这样画出来的是线性的,要想获得db格式的幅度,需要转换 20*log10(Hf)<br />
之后再画就是db格式的<br />
也可以直接用freqz(b,a,w)这样就会画出幅频响应和相频响应,幅频响应直接是db格式的幅度。</p>
涉及PyQt5的一些命令
2017-08-09T00:00:00+00:00
2017-08-09T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-she-ji-pyqt5de-yi-xie-ming-ling/
<p>记录pyqt5中用到的几个命令
安装</p>
<pre><code>pip install python-qt5
</code></pre>
<p>把ui转py</p>
<pre><code>python -m PyQt5.uic.pyuic <arguments>
</code></pre>
<p>lineEdit输入隐藏,密码</p>
<pre><code>lineEdit.setEchoMode(QtWidgets.QLineEdit.Password)
</code></pre>
<p>py转exe</p>
<pre><code>pip install pyinstaller
pyinstaller demo.py
</code></pre>
<p>参数 含义</p>
<hr />
<pre><code>-F 指定打包后只生成一个exe格式的文件
-D --onedir创建一个目录,包含exe文件,但会依赖很多文件(默认选项)
-c --console,--nowindowed使用控制台,无界面(默认)
-F 指定打包后只生成一个exe格式的文件
-w --windowed,--noconsole使用窗口,无控制台
-p 添加搜索路径,让其找到对应的库。
-i 改变生成程序的icon图标
</code></pre>
自己写一个Android照相机应用-1
2017-08-09T00:00:00+00:00
2017-08-09T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-zi-ji-xie-yi-ge-androidzhao-xiang-ji-ying-yong-1/
<p>Android相机的相关知识</p>
<h1 id="android-cameraxiang-guan-zhi-shi">Android Camera相关知识</h1>
<p>调用系统相机</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java">Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//使用action启动系统相机
//startActivity(intent);
//仅调用相机拍照,不返回数据
//startActivityForResult(intent,REQ_1);
//调用相机并通过onActivityResult函数data返回数据;
Uri photoUri = Uri.fromFile(new File(mFilePath));//图片保存路径
intent.putExtra(MediaStore.EXTRA_OUTPUT,photoUri);
startActivityForResult(intent,REQ_2);
//从文件中读取数据
</code></pre>
<p>路径获取</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java">mFilePath = Environment.getExternalStorageDirectory().getPath();
mFilePath = mFilePath + "/" + "tmp.png";
</code></pre>
<p>读取内存卡权限</p>
<pre data-lang="xml" class="language-xml "><code class="language-xml" data-lang="xml"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
</code></pre>
<p>onActivityResult函数</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java">protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK){
if (requestCode == REQ_1) {//从data获取照片
Bundle bundle = data.getExtras();
Bitmap bitmap = (Bitmap)bundle.get("data");
mImageView.setImageBitmap(bitmap);
} else if (requestCode == REQ_2) {//从文件获取照片
FileInputStream fis = null;
try {
fis = new FileInputStream(mFilePath);
Bitmap bitmap = BitmapFactory.decodeStream(fis);
mImageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
</code></pre>
<p>注册action,实现照相功能</p>
<pre data-lang="xml" class="language-xml "><code class="language-xml" data-lang="xml"> <intent-filter>
<action android:name="android.media.action.IMAGE_CAPTURE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</code></pre>
搬瓦工VPS搭建Web服务器
2017-08-04T00:00:00+00:00
2017-08-04T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-ban-wa-gong-vpsda-jian-webfu-wu-qi/
<p>记录在centos上安装apache+php+mysql<br />
搬瓦工VPS<a href="https://bandwagonhost.com/aff.php?aff=17697">购买地址</a></p>
<h1 id="an-zhuang-apache">安装apache</h1>
<p>安装<code>yum install httpd</code><br />
启动<code>service httpd start</code><br />
暂停<code>service httpd stop</code><br />
重启<code>service httpd restart</code></p>
<p>检查开机启动情况<code>chkconfig --list httpd</code><br />
默认情况下是全部关闭的,尽量设置开机启动,以防主机意外关机</p>
<p>设置开机启动命令<code>chkconfig httpd on</code><br />
再次检查启动情况,看到2,3,4,5为启动<br />
关闭开机启动命令<code>chkconfig httpd off</code></p>
<p>到此Apache安装完成,在自己浏览器中输入主机IP和端口号能正常打开</p>
<h1 id="an-zhuang-php">安装php</h1>
<p>命令为:</p>
<pre><code>yum install php
yum install php-mysql php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc
yum install php-mysql
</code></pre>
<p>以上命令为安装php及其支持的组件</p>
<h1 id="an-zhuang-mysql">安装mysql</h1>
<h2 id="an-zhuang">安装</h2>
<hr />
<p>首先查看主机是否已经安装<code>rpm -qa|grep mysql</code><br />
注意:可能检查中安装有php-mysql XXXXX 和
mysql-libXXXX,这是刚刚第二步安装的php的组件,不必理会</p>
<p>已经安装的话就执行删除命令<code>rpm -e mysql</code></p>
<p>然后继续安装<br />
使用命令yum list | grep mysql<br />
来查看yum上提供的mysql数据库可下载的版本</p>
<p>安装命令<code>yum install -y mysql-server mysql mysql-devel</code><br />
等待一段时间安装成功</p>
<h2 id="pei-zhi">配置</h2>
<hr />
<p>接下来是对mysql的配置(注意:命令中是"mysqld",不是mysql)<br />
初始化:<code>service mysqld start</code><br />
重启:<code>service mysqld restart</code><br />
配置开机自动启动:<code>chkconfig mysqld on</code></p>
<p>为mysql的root账户设置密码:<code>mysqladmin -u root password '*********'</code><br />
登录命令<code>mysql -u root -p</code></p>
搬瓦工安装ss并加速
2017-08-03T00:00:00+00:00
2017-08-03T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-ban-wa-gong-an-zhuang-ssbing-jia-su/
<p>通过搬瓦工的VPS安装ss的几种方法</p>
<p>搬瓦工(BandwagonHost)是美国IT7公司旗下的一家提供便宜年付OVZ架构的VPS主机方案的服务商。价格便宜、且依托的商家比较靠谱,具有较高的性价比。拿来搭个FQ是非常合适的,做个小站也是可以的。</p>
<h1 id="1-gou-mai-di-zhi">1.购买地址</h1>
<blockquote>
<p>搬瓦工所有配置及购买地址:<a href="https://bandwagonhost.com/aff.php?aff=17697">https://bandwagonhost.com/aff.php?aff=17697</a>
以前有小内存的VPS年费三刀,很便宜,但现在没有了,缺货,连521M的有时候也会缺货,看来挺火的。</p>
</blockquote>
<h1 id="2-an-zhuang-shadowsocks">2.安装Shadowsocks</h1>
<h2 id="2-1ban-wa-gong-hou-tai-yi-jian-an-zhuang-bu-tui-jian">2.1搬瓦工后台一键安装(不推荐)</h2>
<p>登录账号->Client Area->Services->My Services->KiwiVM Control Panel<br />
来到管理面板,点击 Shadowsocks Server 就可以一键安装 Shadowsocks<br />
测试过它自带的一键安装,速度太慢,yotube视频看不了,有点慢。</p>
<h2 id="2-2shi-yong-shelljiao-ben-an-zhuang-tui-jian">2.2使用shell脚本安装(推荐)</h2>
<p>2.2.1安装 shadowsoksR</p>
<p>Shadowsocks的一个分支,比较不错。但是默认是 aes-256-cfb 加密,如果需要
chacha20 加密,需要手动切换。</p>
<pre><code>wget --no-check-certificate https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocksR.sh
chmod +x shadowsocksR.sh
./shadowsocksR.sh 2>&1 | tee shadowsocksR.log
</code></pre>
<p>安装成功需要等几分钟<br />
这个速度可以看yotube无压力</p>
<p>卸载方法:<br />
使用 root 用户登录,运行以下命令:</p>
<pre><code>./shadowsocksR.sh uninstall
</code></pre>
<p>安装完成后即已后台启动 ShadowsocksR,运行:</p>
<pre><code>/etc/init.d/shadowsocks status
</code></pre>
<p>可以查看 ShadowsocksR 进程是否已经启动。<br />
本脚本安装完成后,已将 ShadowsocksR 自动加入开机自启动。</p>
<p>其它命令:<br />
启动:/etc/init.d/shadowsocks start<br />
停止:/etc/init.d/shadowsocks stop<br />
重启:/etc/init.d/shadowsocks restart<br />
状态:/etc/init.d/shadowsocks status</p>
<p>配置文件路径:/etc/shadowsocks.json<br />
日志文件路径:/var/log/shadowsocks.log<br />
代码安装目录:/usr/local/shadowsocks</p>
<p>2.2.2安装 shadowsocks-go版本</p>
<p>据说有buff,比python版Shadowsocks要快:</p>
<pre><code>wget --no-check-certificate https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocks-go.sh
chmod +x shadowsocks-go.sh
./shadowsocks-go.sh 2&gt;&amp;1 | tee shadowsocks-go.log
</code></pre>
<p>2.2.3其他版本Shadowsocks一键安装:</p>
<p>来自 teddysun:<a href="https://github.com/teddysun/shadowsocks_install">https://github.com/teddysun/shadowsocks_install</a></p>
<h1 id="3-yu-dao-de-yi-xie-wen-ti">3.遇到的一些问题</h1>
<h2 id="3-1ban-wa-gong-mo-ren-xi-tong-centosmei-you-wgetming-ling-xu-yao-an-zhuang">3.1搬瓦工默认系统Centos没有wget命令需要安装</h2>
<p>安装命令:</p>
<pre><code>yum -y install wget
</code></pre>
<h2 id="3-2guan-yu-pei-zhi-duo-yong-hu">3.2关于配置多用户</h2>
<pre><code>vi /etc/shadowsocks.json
</code></pre>
<p>只需要在port_password项下添加"端口号":"密码"</p>
<pre><code>{
"server":"0.0.0.0",
"server_ipv6":"[::]",
"local_address":"127.0.0.1",
"local_port":1080,
"port_password":{
"12345": "123456",
"23456": "123456"
},
"timeout":120,
"method":"aes-256-cfb",
"protocol":"origin",
"protocol_param":"",
"obfs":"plain",
"obfs_param":"",
"redirect":"",
"dns_ipv6":false,
"fast_open":false,
"workers":1
}
</code></pre>
<h1 id="can-kao-zi-liao">参考资料</h1>
<p>1.<a href="https://blog.kuoruan.com/48.html">https://blog.kuoruan.com/48.html</a></p>
Python2与Python3共存
2017-08-03T00:00:00+00:00
2017-08-03T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-python2yu-python3gong-cun/
<p>在windows下安装多个版本的python</p>
<h1 id="an-zhuang-liang-ge-ban-ben-de-python">安装两个版本的Python</h1>
<p>选择想要安装的版本下载例如:<br />
python2下载地址:<a href="https://www.python.org/ftp/python/2.7.13/python-2.7.13.msi">https://www.python.org/ftp/python/2.7.13/python-2.7.13.msi</a><br />
python3下载地址:<a href="https://www.python.org/ftp/python/3.6.2/python-3.6.2.exe">https://www.python.org/ftp/python/3.6.2/python-3.6.2.exe</a></p>
<p>进行安装(安装顺序随便)<br />
尽量安装到同一目录下的两个文件夹例如:<br />
python2安装在"D:\program\python27\"下<br />
python3安装在"D:\program\python36\"下</p>
<h1 id="tian-jia-huan-jing-bian-liang">添加环境变量</h1>
<p>Path中要有一下四个路径</p>
<pre><code>D:\program\python\Python36\Scripts\
D:\program\python\Python36\
D:\program\python\python27\Scripts
D:\program\python\python27\
</code></pre>
<h1 id="geng-gai-python-exeming-zi">更改python.exe名字</h1>
<p>找到找到两个python的安装目录<br />
<code>D:\program\python\Python36\python.exe</code><br />
更改为<br />
<code>D:\program\python\Python36\python3.exe</code><br />
<code>D:\program\python\Python27\python.exe</code><br />
更改为<br />
<code>D:\program\python\Python36\python2.exe</code><br />
此时两个版本的python已经共同存在<br />
但还要对pip进行修改</p>
<h1 id="geng-gai-pipming-cheng">更改pip名称</h1>
<p>重装python2和python3的pip</p>
<pre><code>python2 -m pip install --upgrade pip --force-reinstall
python3 -m pip install --upgrade pip --force-reinstall
</code></pre>
<p>重装成功后进行如下测试<br />
test.PNG...<br />
可见python2和python3已经共存<br />
pip的问题也解决了<br />
但直接调用pip是还能执行并且是后安装的pip的版本<br />
python命令已经不存在<br />
直接运行py结尾的文件时会出现问题<br />
python自带的编辑器出现了两个</p>
JAVA实现MD5加密
2017-07-02T00:00:00+00:00
2017-07-02T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-javashi-xian-md5jia-mi/
<p>java实现MD5加密的两种方法</p>
<p>1,调用MessageDigest实现</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java">import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class CreatMD5 {
public String getMd5(String plainText){
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(plainText.getBytes());
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
//32位加密
return buf.toString();
// 16位的加密
//return buf.toString().substring(8, 24);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
CreatMD5 md=new CreatMD5();
System.out.println(md.getMd5("hello"));
}
}
</code></pre>
<p>2,自己编写函数实现</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java">public class CreateMD5i {
static final String hexs[]={"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
//标准的幻数
private static final long A=0x67452301L;
private static final long B=0xefcdab89L;
private static final long C=0x98badcfeL;
private static final long D=0x10325476L;
//下面这些S11-S44实际上是一个4*4的矩阵,在四轮循环运算中用到
static final int S11 = 7;
static final int S12 = 12;
static final int S13 = 17;
static final int S14 = 22;
static final int S21 = 5;
static final int S22 = 9;
static final int S23 = 14;
static final int S24 = 20;
static final int S31 = 4;
static final int S32 = 11;
static final int S33 = 16;
static final int S34 = 23;
static final int S41 = 6;
static final int S42 = 10;
static final int S43 = 15;
static final int S44 = 21;
//java不支持无符号的基本数据(unsigned)
private long [] result={A,B,C,D};//存储hash结果,共4×32=128位,初始化值为(幻数的级联)
public static void main(String []args){
CreateMD5i md=new CreateMD5i();
System.out.println("md5(hello)="+md.digest("hello"));
}
public String digest(String inputStr){
byte [] inputBytes=inputStr.getBytes();
int byteLen=inputBytes.length;//长度(字节)
int groupCount=0;//完整分组的个数
groupCount=byteLen/64;//每组512位(64字节)
long []groups=null;//每个小组(64字节)再细分后的16个小组(4字节)
//处理每一个完整 分组
for(int step=0;step<groupCount;step++){
groups=divGroup(inputBytes,step*64);
trans(groups);//处理分组,核心算法
}
//处理完整分组后的尾巴
int rest=byteLen%64;//512位分组后的余数
byte [] tempBytes=new byte[64];
if(rest<=56){
for(int i=0;i<rest;i++)
tempBytes[i]=inputBytes[byteLen-rest+i];
if(rest<56){
tempBytes[rest]=(byte)(1<<7);
for(int i=1;i<56-rest;i++)
tempBytes[rest+i]=0;
}
long len=(long)(byteLen<<3);
for(int i=0;i<8;i++){
tempBytes[56+i]=(byte)(len&0xFFL);
len=len>>8;
}
groups=divGroup(tempBytes,0);
trans(groups);//处理分组
}else{
for(int i=0;i<rest;i++)
tempBytes[i]=inputBytes[byteLen-rest+i];
tempBytes[rest]=(byte)(1<<7);
for(int i=rest+1;i<64;i++)
tempBytes[i]=0;
groups=divGroup(tempBytes,0);
trans(groups);//处理分组
for(int i=0;i<56;i++)
tempBytes[i]=0;
long len=(long)(byteLen<<3);
for(int i=0;i<8;i++){
tempBytes[56+i]=(byte)(len&0xFFL);
len=len>>8;
}
groups=divGroup(tempBytes,0);
trans(groups);//处理分组
}
//将Hash值转换成十六进制的字符串
String resStr="";
long temp=0;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
temp=result[i]&0x0FL;
String a=hexs[(int)(temp)];
result[i]=result[i]>>4;
temp=result[i]&0x0FL;
resStr+=hexs[(int)(temp)]+a;
result[i]=result[i]>>4;
}
}
return resStr;
}
/**
* 从inputBytes的index开始取512位,作为新的分组
* 将每一个512位的分组再细分成16个小组,每个小组64位(8个字节)
* @param inputBytes
* @param index
* @return
*/
private static long[] divGroup(byte[] inputBytes,int index){
long [] temp=new long[16];
for(int i=0;i<16;i++){
temp[i]=b2iu(inputBytes[4*i+index])|
(b2iu(inputBytes[4*i+1+index]))<<8|
(b2iu(inputBytes[4*i+2+index]))<<16|
(b2iu(inputBytes[4*i+3+index]))<<24;
}
return temp;
}
/**
* 这时不存在符号位(符号位存储不再是代表正负),所以需要处理一下
* @param b
* @return
*/
public static long b2iu(byte b){
return b < 0 ? b & 0x7F + 128 : b;
}
/**
* 主要的操作,四轮循环
* @param groups[]--每一个分组512位(64字节)
*/
private void trans(long[] groups) {
long a = result[0], b = result[1], c = result[2], d = result[3];
/*第一轮*/
a = FF(a, b, c, d, groups[0], S11, 0xd76aa478L); /* 1 */
d = FF(d, a, b, c, groups[1], S12, 0xe8c7b756L); /* 2 */
c = FF(c, d, a, b, groups[2], S13, 0x242070dbL); /* 3 */
b = FF(b, c, d, a, groups[3], S14, 0xc1bdceeeL); /* 4 */
a = FF(a, b, c, d, groups[4], S11, 0xf57c0fafL); /* 5 */
d = FF(d, a, b, c, groups[5], S12, 0x4787c62aL); /* 6 */
c = FF(c, d, a, b, groups[6], S13, 0xa8304613L); /* 7 */
b = FF(b, c, d, a, groups[7], S14, 0xfd469501L); /* 8 */
a = FF(a, b, c, d, groups[8], S11, 0x698098d8L); /* 9 */
d = FF(d, a, b, c, groups[9], S12, 0x8b44f7afL); /* 10 */
c = FF(c, d, a, b, groups[10], S13, 0xffff5bb1L); /* 11 */
b = FF(b, c, d, a, groups[11], S14, 0x895cd7beL); /* 12 */
a = FF(a, b, c, d, groups[12], S11, 0x6b901122L); /* 13 */
d = FF(d, a, b, c, groups[13], S12, 0xfd987193L); /* 14 */
c = FF(c, d, a, b, groups[14], S13, 0xa679438eL); /* 15 */
b = FF(b, c, d, a, groups[15], S14, 0x49b40821L); /* 16 */
/*第二轮*/
a = GG(a, b, c, d, groups[1], S21, 0xf61e2562L); /* 17 */
d = GG(d, a, b, c, groups[6], S22, 0xc040b340L); /* 18 */
c = GG(c, d, a, b, groups[11], S23, 0x265e5a51L); /* 19 */
b = GG(b, c, d, a, groups[0], S24, 0xe9b6c7aaL); /* 20 */
a = GG(a, b, c, d, groups[5], S21, 0xd62f105dL); /* 21 */
d = GG(d, a, b, c, groups[10], S22, 0x2441453L); /* 22 */
c = GG(c, d, a, b, groups[15], S23, 0xd8a1e681L); /* 23 */
b = GG(b, c, d, a, groups[4], S24, 0xe7d3fbc8L); /* 24 */
a = GG(a, b, c, d, groups[9], S21, 0x21e1cde6L); /* 25 */
d = GG(d, a, b, c, groups[14], S22, 0xc33707d6L); /* 26 */
c = GG(c, d, a, b, groups[3], S23, 0xf4d50d87L); /* 27 */
b = GG(b, c, d, a, groups[8], S24, 0x455a14edL); /* 28 */
a = GG(a, b, c, d, groups[13], S21, 0xa9e3e905L); /* 29 */
d = GG(d, a, b, c, groups[2], S22, 0xfcefa3f8L); /* 30 */
c = GG(c, d, a, b, groups[7], S23, 0x676f02d9L); /* 31 */
b = GG(b, c, d, a, groups[12], S24, 0x8d2a4c8aL); /* 32 */
/*第三轮*/
a = HH(a, b, c, d, groups[5], S31, 0xfffa3942L); /* 33 */
d = HH(d, a, b, c, groups[8], S32, 0x8771f681L); /* 34 */
c = HH(c, d, a, b, groups[11], S33, 0x6d9d6122L); /* 35 */
b = HH(b, c, d, a, groups[14], S34, 0xfde5380cL); /* 36 */
a = HH(a, b, c, d, groups[1], S31, 0xa4beea44L); /* 37 */
d = HH(d, a, b, c, groups[4], S32, 0x4bdecfa9L); /* 38 */
c = HH(c, d, a, b, groups[7], S33, 0xf6bb4b60L); /* 39 */
b = HH(b, c, d, a, groups[10], S34, 0xbebfbc70L); /* 40 */
a = HH(a, b, c, d, groups[13], S31, 0x289b7ec6L); /* 41 */
d = HH(d, a, b, c, groups[0], S32, 0xeaa127faL); /* 42 */
c = HH(c, d, a, b, groups[3], S33, 0xd4ef3085L); /* 43 */
b = HH(b, c, d, a, groups[6], S34, 0x4881d05L); /* 44 */
a = HH(a, b, c, d, groups[9], S31, 0xd9d4d039L); /* 45 */
d = HH(d, a, b, c, groups[12], S32, 0xe6db99e5L); /* 46 */
c = HH(c, d, a, b, groups[15], S33, 0x1fa27cf8L); /* 47 */
b = HH(b, c, d, a, groups[2], S34, 0xc4ac5665L); /* 48 */
/*第四轮*/
a = II(a, b, c, d, groups[0], S41, 0xf4292244L); /* 49 */
d = II(d, a, b, c, groups[7], S42, 0x432aff97L); /* 50 */
c = II(c, d, a, b, groups[14], S43, 0xab9423a7L); /* 51 */
b = II(b, c, d, a, groups[5], S44, 0xfc93a039L); /* 52 */
a = II(a, b, c, d, groups[12], S41, 0x655b59c3L); /* 53 */
d = II(d, a, b, c, groups[3], S42, 0x8f0ccc92L); /* 54 */
c = II(c, d, a, b, groups[10], S43, 0xffeff47dL); /* 55 */
b = II(b, c, d, a, groups[1], S44, 0x85845dd1L); /* 56 */
a = II(a, b, c, d, groups[8], S41, 0x6fa87e4fL); /* 57 */
d = II(d, a, b, c, groups[15], S42, 0xfe2ce6e0L); /* 58 */
c = II(c, d, a, b, groups[6], S43, 0xa3014314L); /* 59 */
b = II(b, c, d, a, groups[13], S44, 0x4e0811a1L); /* 60 */
a = II(a, b, c, d, groups[4], S41, 0xf7537e82L); /* 61 */
d = II(d, a, b, c, groups[11], S42, 0xbd3af235L); /* 62 */
c = II(c, d, a, b, groups[2], S43, 0x2ad7d2bbL); /* 63 */
b = II(b, c, d, a, groups[9], S44, 0xeb86d391L); /* 64 */
/*加入到之前计算的结果当中*/
result[0] += a;
result[1] += b;
result[2] += c;
result[3] += d;
result[0]=result[0]&0xFFFFFFFFL;
result[1]=result[1]&0xFFFFFFFFL;
result[2]=result[2]&0xFFFFFFFFL;
result[3]=result[3]&0xFFFFFFFFL;
}
/**
* 下面是处理要用到的线性函数
*/
private static long F(long x, long y, long z) {
return (x & y) | ((~x) & z);
}
private static long G(long x, long y, long z) {
return (x & z) | (y & (~z));
}
private static long H(long x, long y, long z) {
return x ^ y ^ z;
}
private static long I(long x, long y, long z) {
return y ^ (x | (~z));
}
private static long FF(long a, long b, long c, long d, long x, long s,
long ac) {
a += (F(b, c, d)&0xFFFFFFFFL) + x + ac;
a = ((a&0xFFFFFFFFL)<< s) | ((a&0xFFFFFFFFL) >>> (32 - s));
a += b;
return (a&0xFFFFFFFFL);
}
private static long GG(long a, long b, long c, long d, long x, long s,
long ac) {
a += (G(b, c, d)&0xFFFFFFFFL) + x + ac;
a = ((a&0xFFFFFFFFL) << s) | ((a&0xFFFFFFFFL) >>> (32 - s));
a += b;
return (a&0xFFFFFFFFL);
}
private static long HH(long a, long b, long c, long d, long x, long s,
long ac) {
a += (H(b, c, d)&0xFFFFFFFFL) + x + ac;
a = ((a&0xFFFFFFFFL) << s) | ((a&0xFFFFFFFFL) >>> (32 - s));
a += b;
return (a&0xFFFFFFFFL);
}
private static long II(long a, long b, long c, long d, long x, long s,
long ac) {
a += (I(b, c, d)&0xFFFFFFFFL) + x + ac;
a = ((a&0xFFFFFFFFL) << s) | ((a&0xFFFFFFFFL) >>> (32 - s));
a += b;
return (a&0xFFFFFFFFL);
}
}
</code></pre>
谷歌浏览器运行安卓APK
2017-06-25T00:00:00+00:00
2017-06-25T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-gu-ge-liu-lan-qi-yun-xing-an-zhuo-apk/
<p>使用谷歌浏览器的插件ARChon实现安卓APK在电脑上运行</p>
<p>用谷歌插件在电脑上运行安卓APK</p>
<p>1,直接谷歌应用商店下载出现错误<br />
程序包无效:"CRX_SIGNATURE_VERIFICATION_FAILED"。</p>
<p>2,直接下载crx包参考百度经验</p>
<p>3,出现错误:Cannot load extension with file or directory name
<em>metadata. Filenames starting with "</em>" are reserved for use by the
system.<br />
更改解压后文件夹_metadata为metadata</p>
<p>4,出现错误:'import' extension is not installed.<br />
打开文件manifest.json去掉import一句</p>
<pre><code>"import":[{"id":"mfaihdlpglflfgpfjcifdjdjcckigekc"}],
</code></pre>
<p>继续谷歌去掉这句话不对<br />
看这篇CSDN文章<br />
说要下载插件ARChon<br />
(以下废话,直接看5)</p>
<p>4.1,安装成功,但启用后一直白屏</p>
<p><img src="http://upload-images.jianshu.io/upload_images/2950376-a275f201f7fdead8.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" /></p>
<p>怀疑是不是去掉import一句出问题了</p>
<p>4.2,网上看到开启Native Client<br />
谷歌浏览器地址栏输入chrome://flags/开启Native Client<br />
无用</p>
<p>4.3,发现另一个谷歌商店的应用twerk这个应用是把 APK 文件转换成 Chrome
App用的</p>
<p><img src="http://upload-images.jianshu.io/upload_images/2950376-2372cf692c93b5a9.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" /></p>
<p>5ARChon是谷歌插件要在扩展程序,打钩开发者模式,点击加载已解压扩展程序,选择解压文件夹<br />
加载ARChon时有警告,可忽略<br />
先加载ARC Welder出现4的错误后<br />
再加载ARChon,然后ARC Welder就好了,不好的话重新加载就好了<br />
安装好了就是APK运行不了</p>
<p>4.3安装的twerk可以把 APK 文件转换一个文件夹<br />
然后ARC Welder运行<br />
谷歌浏览器如何运行APK博客CSDN可以试一试</p>
<h2 id="you-yu-dao-de-wen-ti">又遇到的问题</h2>
<p>Chrome加载Android应用,提示"There is no "message" element for key
extName"错误,如:</p>
<p><img src="http://upload-images.jianshu.io/upload_images/2950376-0c0702ed3e06b504.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" /></p>
<h2 id="jie-jue-fang-fa">解决方法:</h2>
<p>找到该Crx文件夹下的"_locales\en"目录下的"messages.json"文件,在"extName"节点下,添加"message"字段标签,值对应应用的包名:</p>
<p><img src="http://upload-images.jianshu.io/upload_images/2950376-c5a40ac2756b5a87.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" /></p>
<p>WebGL 不受支持<br />
知乎有教程<br />
浏览器快捷方式属性增加---ignore-gpu-blacklist<br />
开启硬件加速<br />
试了一下掌阅</p>
<p><img src="http://upload-images.jianshu.io/upload_images/2950376-f4ed0ddb2b2d06e1.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" /></p>
<p>试了一下简书<br />
可以运行</p>
<p><img src="http://upload-images.jianshu.io/upload_images/2950376-ed37c301d49d7485.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="" /></p>
批处理for循环使用
2017-04-22T00:00:00+00:00
2017-04-22T00:00:00+00:00
Unknown
https://blog.vhcffh.com/2017/hexo-pi-chu-li-forxun-huan-shi-yong/
<p>FOR %variable IN (set) DO command [command-parameters]
variable是一个变量
(set)相当于一个数组,variable遍历这个数组
command [command-parameters]这是每次循环要做的事</p>
<h3 id="zai-cmdzhong-shu-ru-shi-yong-shi-ying-xie-wei">在cmd中输入使用时应写为</h3>
<pre><code>FOR %variable IN (set) DO command [command-parameters]
</code></pre>
<p>command [command-parameters]中用到变量也要写为%variable</p>
<h3 id="xie-jin-bathuo-zhe-cmdshi-ying-xie-wei">写进.bat或者.cmd时应写为</h3>
<pre><code>FOR %%variable IN (set) DO command [command-parameters]
</code></pre>
<p>command [command-parameters]中用到变量也要写为%%variable</p>
<h3 id="windowszi-dai-de-helpjie-shi">Windows自带的help解释</h3>
<pre><code>FOR %variable IN (set) DO command [command-parameters]
%variable 指定一个单一字母可替换的参数。
(set) 指定一个或一组文件。可以使用通配符。
command 指定对每个文件执行的命令。
command-parameters 为特定命令指定参数或命令行开关。
</code></pre>
<p>在批处理程序中使用 FOR 命令时,指定变量请使用 %%variable
而不要用 %variable。变量名称是区分大小写的,所以 %i 不同于 %I.</p>
<p>如果启用命令扩展,则会支持下列 FOR 命令的其他格式:</p>
<pre><code>FOR /D %variable IN (set) DO command [command-parameters]
</code></pre>
<p>如果集中包含通配符,则指定与目录名匹配,而不与文件名匹配。</p>
<pre><code>FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]
</code></pre>
<p>检查以 [drive:]path 为根的目录树,指向每个目录中的 FOR 语句。
如果在 /R 后没有指定目录规范,则使用当前目录。如果集仅为一个单点(.)字符,则枚举该目录树。</p>
<pre><code>FOR /L %variable IN (start,step,end) DO command [command-parameters]
</code></pre>
<p>该集表示以增量形式从开始到结束的一个数字序列。因此,(1,1,5)将产生序列1 2 3 4 5,(5,-1,1)将产生序列(5 4 3 2 1)</p>
<pre><code>FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]
</code></pre>
<p>或者,如果有 usebackq 选项:</p>
<pre><code>FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]
</code></pre>
<p>fileset 为一个或多个文件名。继续到 fileset 中的下一个文件之前,每份文件都被打开、读取并经过处理。处理包括读取文件,将其分成一行行的文字,然后将每行解析成零或更多的符号。然后用已找到的符号字符串变量值调用 For 循环。
以默认方式,/F 通过每个文件的每一行中分开的第一个空白符号。跳过空白行。你可通过指定可选 "options" 参数替代默认解析操作。这个带引号的字符串包括一个 或多个指定不同解析选项的关键字。这些关键字为:</p>
<pre><code> eol=c - 指一个行注释字符的结尾(就一个)
skip=n - 指在文件开始时忽略的行数。
delims=xxx - 指分隔符集。这个替换了空格和制表符的
默认分隔符集。
tokens=x,y,m-n - 指每行的哪一个符号被传递到每个迭代
的 for 本身。这会导致额外变量名称的分配。m-n
格式为一个范围。通过 nth 符号指定 mth。如果
符号字符串中的最后一个字符星号,
那么额外的变量将在最后一个符号解析之后
分配并接受行的保留文本。
usebackq - 指定新语法已在下类情况中使用:
在作为命令执行一个后引号的字符串并且一个单
引号字符为文字字符串命令并允许在 file-set
中使用双引号扩起文件名称。
</code></pre>
<p>某些范例可能有助:</p>
<pre><code>FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k
</code></pre>
<p>会分析 myfile.txt 中的每一行,忽略以分号打头的那些行,将每行中的第二个和第三个符号传递给 for 函数体,用逗号和/空格分隔符号。请注意,此 for 函数体的语句引用 %i 来获得第二个符号,引用 %j 来获得第三个符号,引用 %k来获得第三个符号后的所有剩余符号。对于带有空格的文件名,你需要用双引号将文件名括起来。为了用这种方式来使用双引号,还需要使用 usebackq 选项,否则,双引号会被理解成是用作定义某个要分析的字符串的。%i 在 for 语句中显式声明,%j 和 %k 是通过 tokens= 选项隐式声明的。可以通过 tokens= 一行指定最多 26 个符号,只要不试图声明一个高于字母 "z" 或 "Z" 的变量。请记住,FOR 变量是单一字母、分大小写和全局的变量;而且,不能同时使用超过 52 个。
还可以在相邻字符串上使用 FOR /F 分析逻辑,方法是,用单引号将括号之间的 file-set 括起来。这样,该字符串会被当作一个文件中的一个单一输入行进行解析。</p>
<p>最后,可以用 FOR /F 命令来分析命令的输出。方法是,将括号之间的 file-set 变成一个反括字符串。该字符串会被当作命令行,传递到一个子 CMD.EXE,其输出会被捕获到内存中,并被当作文件分析。如以下例子所示:</p>
<pre><code> FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i
</code></pre>
<p>会枚举当前环境中的环境变量名称。</p>
<p>另外,FOR 变量参照的替换已被增强。你现在可以使用下列
选项语法:</p>
<pre><code> %~I - 删除任何引号("),扩展 %I
%~fI - 将 %I 扩展到一个完全合格的路径名
%~dI - 仅将 %I 扩展到一个驱动器号
%~pI - 仅将 %I 扩展到一个路径
%~nI - 仅将 %I 扩展到一个文件名
%~xI - 仅将 %I 扩展到一个文件扩展名
%~sI - 扩展的路径只含有短名
%~aI - 将 %I 扩展到文件的文件属性
%~tI - 将 %I 扩展到文件的日期/时间
%~zI - 将 %I 扩展到文件的大小
%~$PATH:I - 查找列在路径环境变量的目录,并将 %I 扩展
到找到的第一个完全合格的名称。如果环境变量名
未被定义,或者没有找到文件,此组合键会扩展到
空字符串
</code></pre>
<p>可以组合修饰符来得到多重结果:</p>
<pre><code> %~dpI - 仅将 %I 扩展到一个驱动器号和路径
%~nxI - 仅将 %I 扩展到一个文件名和扩展名
%~fsI - 仅将 %I 扩展到一个带有短名的完整路径名
%~dp$PATH:I - 搜索列在路径环境变量的目录,并将 %I 扩展
到找到的第一个驱动器号和路径。
%~ftzaI - 将 %I 扩展到类似输出线路的 DIR
</code></pre>
<p>在以上例子中,%I 和 PATH 可用其他有效数值代替。%~ 语法
用一个有效的 FOR 变量名终止。选取类似 %I 的大写变量名
比较易读,而且避免与不分大小写的组合键混淆。</p>