曹逸君 Blog

12月 28, 2014

12月 02, 2014

安卓九宫格的锁屏图案的最长长度

题干很简单.求安卓九宫格的锁屏图案的最长长度.

这里重新描述下九宫格锁屏图案规则.

  1. 九宫格,3x3的方形点阵
  2. 所有点只能经过一次
  3. 点与点间连线是直线
  4. 不能跨过未经过的点去链接,否则视为先链接中间点再链接对面点.

有些锁屏要求至少链接4个点之类的.与本题无关.忽略.

以有向图建立模型的描述为:

  1. 3x3/9个节点.Id从0到8.
  2. 起始点入度为0出度为1.截至点入度为1出度为0.
  3. 除起始和截至点外,节点入度和出度均为1.[由于本题要链接所有9个节点]
  4. 每个节点到其他所有节点的距离都固定且给出.[先忽略规则3.简化起见]

生成邻接矩阵

[   [0.000, 1.000, 2.000, 1.000, 1.414, 2.236, 2.000, 2.236, 2.828],
    [1.000, 0.000, 1.000, 1.414, 1.000, 1.414, 2.236, 2.000, 2.236],
    [2.000, 1.000, 0.000, 2.236, 1.414, 1.000, 2.828, 2.236, 2.000],
    [1.000, 1.414, 2.236, 0.000, 1.000, 2.000, 1.000, 1.414, 2.236],
    [1.414, 1.000, 1.414, 1.000, 0.000, 1.000, 1.414, 1.000, 1.414],
    [2.236, 1.414, 1.000, 2.000, 1.000, 0.000, 2.236, 1.414, 1.000],
    [2.000, 2.236, 2.828, 1.000, 1.414, 2.236, 0.000, 1.000, 2.000],
    [2.236, 2.000, 2.236, 1.414, 1.000, 1.414, 1.000, 0.000, 1.000],
    [2.828, 2.236, 2.000, 2.236, 1.414, 1.000, 2.000, 1.000, 0.000]]

9x9的图看起来吃力.我们看张4x4的.

[   [0.000, 1.000, 1.000, 1.414],
    [1.000, 0.000, 1.414, 1.000],
    [1.000, 1.414, 0.000, 1.000],
    [1.414, 1.000, 1.000, 0.000]]

graph[i][j] 代表的就是节点i到节点j的距离.

所以第一行就是节点0到各个节点的距离.由于我们在求最长距离.故将自己到自己的距离设为0.以免选到自己到自己的路径.

在不考虑规则3的情况下.这是个类似于N皇后的问题.

注意这里贪心算法并不能保证给出最优解.[虽然在4宫格的情况下可以.]

使用回朔算法.

求解大致过程:

  1. 给定起始节点0.
  2. 将第0列设为0[起始节点不能到达.]
  3. 分别考虑第0行所有非0值.[复制当前邻接矩阵] 3.1 选取节点1作为下一个节点. graph[0][1]非零,将graph[0][1]加到长度记录中. 3.2 将第0行以及第1列设为0.因为节点0不能再出发.节点1不能再到达. 3.3 重复此过程直至邻接矩阵全部为0[所有节点都已访问.]
  4. 保存当前最优解.

不考虑规则3的解

我闷直接看得到的解吧. 从1出发:

solution1_unrestricted.png

这里直接穿过4从6到达2. 4是最后访问的.

如果没有规则3.我们还可以把所有距离取负值[或者修改松弛过程使得松弛后距离变长]的Dijkstra算法来更快的求解.

由于规则3的存在.使用更通用的回朔算法.

下面增加规则3. 使用一个额外的access_graph来控制是否可以访问.

access_graph = [[1 for _ in range(9)] for _ in range(9)]
breaks =    {   1: [(0, 2)],
                3: [(0, 6)],
                5: [(2, 6)],
                7: [(6, 8)],
                4: [(0, 8), (1, 7), (2, 6), (3, 5)]
                }
for i,j in flaten(breaks.values()):
    access_graph[i][j] = 0
    access_graph[j][i] = 0

并且当1 3 5 7 4这些节点被链接后将根据breaks使被阻拦的点可以访问.

考虑规则3后的最终解

best_solution.png

这就是最终的解. 长度为17.779271744364845

$$\sqrt { 2 } +\sqrt { 8 } +\sqrt { 5 } +2+\sqrt { 5 } +\sqrt { 8 } +\sqrt { 5 } +2$$

[(4, 8), (8, 0), (0, 7), (7, 1), (1, 6), (6, 2), (2, 3), (3, 5)] 从4开始.到5结束.

后话

相关问题: 安卓九宫格密码有多少种组合?--知乎

智能手机的密码总共有多少种--果壳--Matrix67

关于动态图最长/短路径.请搜索Dynamic Algorithms Maintaining Shortest Paths

刚刚看到个小盆友说自己没流量于是只好玩锁屏界面.发现说连线不重复交点最多只有7个..真是naive.

most_intersects.png

随手画一个就有12个交点.[我感觉应该是交点最多的了.没验证]

Written with StackEdit.

11月 18, 2014

Python pdb 调试 自动异常中断

Python pdb 调试 自动异常中断

我通常不愿意跑pydev或者pycharm这类庞大的IDE. 个人认为IDE提供的最有价值的功能是方便的调试. Python自带一个调试库pdb.

pdb常规用法

你可能已经在这样使用pdb

import pdb

def func_to_debug(*arg, **kwarg):
    """doc"""
    ...
    pdb.set_trace()
    #Suspicious code
    ...

在python解释器运行的时候pdb.set_trace()会将代码中断,然后在pdb的交互界面中可以使用pdb的 交互命令 进行 pdb中可以执行任意python代码,对已有对象进行的修改也会直接生效. 输出对象 pp obj.__dict__['some_property'] 查看源码 list or l 查看调用栈 where or w 转到上级调用(step out) up or u 转到下级调用(step in) down or d 退出pdb quit or q

注意因为列表(list)和列出源码的命令list冲突.所以使用列表需要用 __builtins__.list

但代码抛出异常时我们还需要加入pdb.set_trace() 然后再次运行代码去debug. 这种动作重复十几次之后你就会感到厌烦. 事实上是可以做到抛出异常时自动进入pdb的. 在上层调用try..except 来捕获异常并中断即可. 敲try..except 调好再去掉十几次之后你就会感到厌烦.于是我们做成一个装饰器(decorator).

扔出异常时自动中断

准备代码:

import pdb, traceback, sys

def debug(func):
    def wrap(*arg, **kwarg):
        try:
            return func(*arg, **kwarg)
        except:
            type, value, tb = sys.exc_info()
            traceback.print_exc()
            pdb.post_mortem(tb)
    return wrap

这里debug作为装饰器(decorator)使用.将目标函数进行try..except包裹,抛出异常时中断. 使用:

@debug
def func_to_debug(*arg, **kwarg):
    ...

这样抛出异常就直接进入到pdb中断.无需重新运行代码(set up). 添加/去除调试也更加方便.(注释掉@debug)

作为package方便引用

当把debug装饰器代码复制十几次之后,你会感到厌烦. 于是我们在python库目录(Linux: /usr/local/lib/python2.7/dist-packages/)里新建目录debug

把debug脚本放进去debug.py

再在里面建立一个空的__init__.py 文件来告诉python这是个package.

最后再chown -R改变整个目录的所有者避免权限问题.

更改 /usr/local/lib/python2.7/dist-packages/ 需要root权限

然后在你的项目里就可以方便的引用debug装饰器了. from debug.debug import debug

其他信息

注意当你发布的时候他人的机器很可能没有debug这个package. 要么去除debug代码.要么一起打包. 用于release的工具: mr.developer

其他pdb替代品.(第三方库,需要安装)

  • ipdb (pip install ipdb) - like ipython (autocomplete, colors etc).
  • pudb (pip install pudb) - curses based (gui-like), good at browsing sourcecode.
  • pdb++ (pip install pdbpp) - autocomplete, colors, extra commands etc.

更多调试工具 图形化分析工具

Written with StackEdit.

10月 02, 2014

Ubuntu 基本操作

Ubuntu 基本操作

(本文为谷歌营全员学习计划Linux教学的一部分)

事实上接下去讲的这些命令nix系统都通用。类Unix[Unix-like,也写做nix]。包括了Linux、Unix的家族。Mac所使用的OS X是基于Unix的,所以也是Unix-like的操作系统。

但如果是链接到一台服务器上基本就得纯命令行界面操作了[当然也可以用类似远程桌面的X-server转发]。

关于逼格

一般人看到看不懂,别人操作很溜的东西会觉得炫酷、逼格高。注意这是外行的看法,用久了之后就会开始觉得有些地方还是不爽,要是怎样怎样就好了。建议大家不要过多的追求逼格,任何东西方便好用是第一位的。

打开bash

Ubuntu下通过快捷键ctrl+alt+t打开终端。 或者通过Ubuntu左上角的类似于Windows的开始按钮点开Dashboard.输入terminal。其实只要输入ter就能搜索到"终端"

dash_terminal

打开后可以看到用户名@主机名:当前目录$的样子。如下,我的用户名是c19。主机名是Acer。当前目录是~ 也就是/home/c19的缩写。

bash

目录结构

类Unix系统中不存在windows的盘符概念。分区是通过/media/用户名/分区名 来访问的。Ubuntu中可以在文件管理器左侧看到。

file_explorer

类Unix系统中所有目录起始于根目录 用/表示

direcotries_structure

在文件管理其中按ctrl+l显示当前目录的绝对路径(可以理解为完整的路径)。 也就是/media/c19/B208769308765677/

absolute path

可以看到这是我windows的C盘。

目录基本命令

现在再在终端下访问这个windows的分区。打开终端[快捷键Ctrl+Alt+T]。

bash_directory_operations

pwd 显示当前目录的绝对路径 present working directory ls 列出当前目录下的文件和目录 [试试llla 以及 ls -a] cd 目录名 切换目录 change directory tab键 Q左边的那个键。在命令行下常用于补全命令。如上只需打出命令或目录的前几个字母,按下tab便会将其补全。连按两下则显示以这几个字母开头的命令、目录、文件等。 Trick: cd $OLDPWD 回到上次目录

软件管理基本命令[包管理器]

*nix的软件有一个统一的软件源。由发行版的维护方维护。及时更新软件、检查兼容性、安全性等等。而安装软件、管理软件版本以及依赖关系[nix下的软件经常会依赖于各种库或其他软件,手动去安装依赖包是件很费事费力痛苦的事情,于是就有了包管理器自动解决这些问题。]。 Debian的包管理器是apt Red-Hat的是yum,以下以apt为例。 apt-get update 更新源的软件信息。这样才知道有什么新软件、新版本。 Linux下管理软件是需要root权限的。在终端输入sudo -s以将此终端提升为root权限。或者在每条语句前加上sudo 注意sudo后有空格。

sudo -s

apt-get install gcc g++ 安装或升级gcc和g++

apt-get remove gcc g++ 删除gcc和g++

这里的apt-get install remove gcc等都是可以通过tab补全的。 Trick: sudo !! 双感叹号是上次执行的命令。

apt_install_pip

一般的软件都可以在终端中直接运行,就像命令一样。并且一般的命令行程序都会有-V--version来得到版本信息。以及-h--help获得帮助的参数。这是个约定俗成的习惯。查看更详细的手册使用man 命令。但其实第一反映是会去google搜索。

Written with StackEdit.

shell bash 终端 命令行 名词解释

<shell bash 终端 命令行> 名词解释

(本文为谷歌营全员学习计划Linux教学的一部分)

shell bash 终端 命令行

这几个名词本来是不同/不同层次的东西。但经常被混淆。

shell

在图形界面出现之前人们用电脑都是在这么一个黑底白字的界面里敲命令,然后它返回结果的。这个界面其实也是个程序,这程序被称为shell。

linux_0.01

更广义一点,用来调用其他程序的程序称为shell。比如远程控制软件,你可以通过它执行任意程序/指令。会被称为shell。

bash

早期Unix的一个shell。目前Linux和OS X都将它作为默认shell。[当然bash也是更新到现在,一成不变肯定是要被淘汰的。]

终端(Terminal)

90年代及以前的时候大家用的电脑通常不是一台完整的电脑。而是有屏幕和键盘。然后链接到共用的主机上使用主机运行程序的。也有自己电脑有一定运算储存能力但比较弱,而且没联网。需要运行比较大的程序或想上网时链接到主机上操作主机。用来操作主机的设备就叫终端。到后来这种用主机的方式逐渐消失。终端一词的含义就变为了用于操作自己电脑的那个shell了。

命令行(Command Line)

也称命令提示符(Command Line Interpreter/Promote) 所有文字界面的程序都可以称为命令行。比如mysql、mongo。但由于windows的shell cmd.exe 被windows称为命令行,习惯于windows的人会用命令行来代指终端。另外cmd.exe 不是dos。只是长得和dos的shell像。

控制台(console)

还有个名词叫控制台(console),这个词是在windows下指运行在黑框框里的命令行程序。一般C/C++没写图形的程序都是这种控制台程序(感觉怪怪的,大家叫它命令行程序比较多。)。

Written with StackEdit.

9月 28, 2014

Linux 历史简介

Linux 历史简介

关于Linux的起源以及Linus的故事。

(本文为谷歌营全员学习计划Linux教学的一部分)

关于Linux的详细历史可以看<Just for Fun> Linus自传。

起因

大概是1991年,芬兰大学生Linus Torvalds同学觉得Unix太特么贵了。自己买不起,自己从小就倒腾各种电子产品,敲代码,就自己动手花了几个月仿照Unix写了个操作系统。

并通过邮件列表和BBS发布了出去。由于十分的不完善,Linus同学把它标记为0.01版。

一开始长这样。

linux_0.01

早期所有操作系统的界面都是这幅样子。你输入一个命令进去。比如ls,回车。它就返回当前目录下所有文件。

linux_0.01_commands

这些都是0.01所支持的命令。其实就是一个个小程序。

whoami 这个很有意思,返回当前的用户名。【比较严谨的黑客电影里有时会出现这个命令 $whoami root root是Linux自带的最高权限用户】

*nix系统设计理念

这种一个个小程序只做一小部分事情,然后通过组合来完成更复杂事情的思想是C语言创始人Dennis Ritchie,在贝尔实验室经历过一次想做大而全操作系统的失败后所做的Unix建立的思想。

*nix中让每个程序相互合作的方式是标准输入输出和管道

比如查看当前进程的命令是ps,但我想找出某个进程的信息,就可以通过grep[类似于搜索的命令]来搜索。像这样: ps -ef|grep python

Linux快速的发展

恩,回到正题,Linus同学的Linux 0.01 虽然是抄袭Unix,也很不完善,但是它诸多碉堡的地方比如支持多线程。当时大家用的Minix【Unix的一支】一次只能运行一个程序,并且,任何程序出错,整个操作系统都会崩溃得重启。

Linus本来以为这个自己做的小项目只会维持几个月。但外界热情的反馈和Linux的快速成长都让他停不下来。

到了1992年,Linux已经比Minix更受欢迎。这搞得Minix作者很不爽,在网上跟Linus吵嘴架,后来Linus吵最架的能力逐年高升,经常在邮件列表里爆别人菊花。

后面的发展我就不细说了。基本上是Unix在打官司、微软和苹果搞起图形,IBM由于硬件厂商体质操作系统层面由微软靠商业操作占领PC端,Mac封闭式体系占有率不及微软[主要初期不兼容当时巨头IBM的PC],Linux成为全球最大开源合作项目,由于图形界面发展慢。不得PC端。占领了大块服务器份额。

开源的思想

开源通常意味着 1.使用它不用付费或受任何其他限制。 2.你可以得到它的源码,并做任意更改。重新发布。 所以你也可以找个发行版改个名字重新发布。当然这种程度的改动只会被其他人嘲笑而不会有人去使用它。

开源也有多种不同的协议版本。Linux所使用的GPL最狠。除了可以对程序+源码想干嘛干嘛外,如果你写的程序中含有使用GPL协议的代码,你的程序也必须使用GPL协议。

gpl_mit.jpg

不必详细记住。知道GPL最严格。MIT最最随意即可

正是由于开源的自由主义理念,才得以让Linux在全球最顶尖的Coders&Hackers维护更新下茁壮成长,发展出庞大的家族以及走在业界最前列。比如OS X上一个版本Mavericks新加的内存压缩技术。在这四年前Linux中就出现了。还有更碉堡的CPU、内存热插拔。

至今Linux的内核仍在以每天都有新代码提交的速度更新。https://github.com/torvalds/linux [注:linux内核是后来迁入github的。linux的历史可要比github早好多。事实上git就是Linux们为了方便如此大规模的协同开发而开发的。]

Written with StackEdit.

9月 27, 2014

Linux 常识普及

Linux 常识普及

(本文为谷歌营全员学习计划Linux教学的一部分)

本文旨在给不知道的Linux的人提供基本的概念。

Linux 是个操作系统内核

什么是操作系统内核?

内核提供操作系统最重要和基本的功能,管理文件系统、管理内存、进程、以及硬件设备

什么是发行版

Windows的版本更新是内核和外壳[外壳这个很不专业的词别拿出去用]一起的。xp=>win7=>win8,感受不到内核和外壳的差别。 而Linux活跃的发行版本就有十几个。用的内核都是Linux[版本可能不同]。 发行版本的概念可以类比下windows用来做定价区分并无实质差别的家庭版、专业版、旗舰版。 两大发行版:

Debian

redhat

基于Debian的发行版中知名度比较高的当数Ubuntu,Ubuntu由于背后canonical公司的更新维护,友好的桌面环境/图形界面,已经成为用户数最多的Linux桌面系统了。

Ubuntu

基于RedHat,并移除了商业闭源软件的CentOS占据了大量服务器的份额。

什么是桌面环境?

你可以理解为管理图形界面的程序,其实还包括与之相关的一些软件。

发行版之间有什么区别?

大支之间[Debian、Redhat、Slackware]一般包管理[即软件管理]不同。 其他比如Lubuntu就把Ubuntu的一些自带软件更换了一下、桌面环境从gnome换成Lxde。gobuntu作为谷歌定制的系统。增进了安全性。去掉了一些可能产生漏洞的软件。还有之前国内炒的"国产操作系统",也只是拿Linux内核做了个发行版。[或者直接改的别的发行版?]

为什么有这么多发行版?/什么是开源?

因为Linux从一开始就是开源的。 开源通常意味着 1.使用它不用付费或受任何其他限制。 2.你可以得到它的源码,并做任意更改。重新发布。 所以你也可以找个发行版改个名字重新发布。当然这种程度的改动只会被其他人嘲笑而不会有人去使用它。

为什么用Linux/用Linux干嘛

适宜的编程环境&熟悉服务器所用的系统

刚用Linux的时候我因为没扣扣、迅雷而痛苦。如果你是为了提升逼格[像我当初那样],请做好用webqq/开虚拟机跑QQ迅雷/用网页云播的心里准备。 Linux应用面很广,从手表到超级计算机到服务器以及桌面系统都有。一般多指服务器和桌面系统,因为这两个接触的人群更多一点。 我们一般用Linux除了提升逼格外很重要的是有个更适宜的编程环境,以及熟悉服务器所用的系统。便于维护服务器[更新个nginx、写个监测脚本发警报邮件之类的]。 一般装自己PC上推荐Ubuntu。桌面环境友好,易于上手。而服务器方面使用Ubuntu的比例正在上涨。毕竟相比CentOS,Ubuntu更新更快,软件源更新。

Written with StackEdit.

6月 17, 2014

看的懂的动态规划解释(动态规划求解0/1背包问题)

网上看到的中文对动态规划的解说以及样例让我很火大。不说清楚总体思路就跳到细节里。给的代码也是用尽没有说明的单字母缩写。不明所以的循环。 而维基你懂得。你要看懂一篇,先得把里面专业名词,各种数学全了解遍。

以0/1背包问题为例。先说明问题。【我就不用背景故事了。钻石还是砖头都无所谓】 有N个物品。每个物品有两个属性。它所占的空间或着说重量weight.和它的价值value。 和一个容器[背包],背包有最大容量Capacity。求此背包能放下的东西的最大价值。

先看下暴力搜索是怎么处理的,把能放进去的物品的每种放法全部穷举一遍。找出最大值。 而贪心则是先放性价比最高的[value/weight最大]。再放其次。依次类推。

动态规划,带着如此高端大气上档次的名字。其实很简单,是做出一张表,得到最大值。 表如下:

物品1   只有物品1能达到的最大值                  
物品2   只有物品1和物品2能达到的最大值                  
物品3   有物品1和物品2和物品3能达到的最大值                   
物品4   有物品1、2、3、4能达到的最大值                 
...                     
物品n                     
        背包容量为1  2   3   4   ... Capacity

动态规划先把只有物品1在不同容量下能达到的最大价值列举出来。 看具体例子,有以下四个物品。3h什么的是weight[或者说cost]。100€是价值(value)。

A 3h 100€
B 5h 120€
C 4h 80€
D 1h 50€

那么在计算第一行,只有A物品的情况后得到的表是这样子的:

A   0   0   100 100 100 100 100 100 
B   0   0   0   0   0   0   0   0   
C   0   0   0   0   0   0   0   0   
D   0   0   0   0   0   0   0   0   
容量 1   2   3   4   5   6   7   8

当然,程序里是没有第一列的ABCD和底下一行的容量的。那是我为了标注清楚加上去的。

第一行。只有物品A。在容量为1和2时,放不下,故最大价值为0。容量3到8只能放A。最大价值为100。

A   0   0   100 100 100 100 100 100 
B   0   0   100 100 120 120 120 220 
C   0   0   0   0   0   0   0   0
D   0   0   0   0   0   0   0   0   
容量 1   2   3   4   5   6   7   8

注意,这里是动态规划的精华部分。

现在计算第二行。此时新加入物品B 5h 120€。

[注意动态规划并不关心之前是几个物品,怎么放的。只要知道上一行(在没加此行新物品时)在不同容量下的最大价值即可。]。

当容量是1到4时,是不可能放下物品B的。故跟没有物品B的最大价值一致。也就是跟上面一格值相同。 当容量是5时,有几种选择。

1.不放B,价值为上面一格,也就是100.

2.放B,价值为: B的价值120 加上 在没有B、容量为0情况下的最大价值[也就是上一行第0列],也就是0。得到120.

当前容量5减掉B的重量(weight)5 得到的0。 选择两种情况种大的,便是容量为5,新加B情况下的最大价值。

我们再来看第二行最后一个220是如何得到的。[显然它是同时放了A和B,但重点是我们要理解动态规划是如何得到这个值的]

此时计算的是容量为8,新加物品B 5h 120€。

1.不放B,价值为上面一格,也就是100.

2.放B,价值为: B的价值120 加上 在没有B、容量为3情况下的最大价值[也就是上一行第3列],也就是100。得到220.

当前容量8减掉B的重量(weight)5 得到的3。 为什么要加上没有B。容量为(当前容量减B重量)的值?因为这样子就可以得到把B放进去得到的最大值了。

重复这一过程就能把整个表完成。而右下那个格子就是我们要求的情况。有n个物品。容量为Capacity所能达到的最大价值。 贴一下中间过程。可以看着思索下。脑中模拟下。

0   0   0   0   0   0   0   0   0   
0   0   0   100 100 100 100 100 100 
0   0   0   0   0   0   0   0   0   
0   0   0   0   0   0   0   0   0   
0   0   0   0   0   0   0   0   0   

0   0   0   0   0   0   0   0   0   
0   0   0   100 100 100 100 100 100 
0   0   0   100 100 120 120 120 220 
0   0   0   0   0   0   0   0   0   
0   0   0   0   0   0   0   0   0   

0   0   0   0   0   0   0   0   0   
0   0   0   100 100 100 100 100 100 
0   0   0   100 100 120 120 120 220 
0   0   0   100 100 120 120 180 220 
0   0   0   0   0   0   0   0   0   

0   0   0   0   0   0   0   0   0   
0   0   0   100 100 100 100 100 100 
0   0   0   100 100 120 120 120 220 
0   0   0   100 100 120 120 180 220 
0   50  50  100 150 150 170 180 230 

Cars chosen to repair:
D 1h 50€
C 4h 80€
A 3h 100€
Total revenue:
230

多出来的第一行0和第一列0是为了让下标符合题目的语义。容量1就是下标1.物品1就是下标1。

关于代码见https://github.com/c19/java_exercise/tree/master/car_repair

相信看懂后不看代码也能顺利写出动态规划。

误解与选法倒推

请务必牢记价值表的含义。

动态规划在计算每个格子的时候,考虑的都只是那个格子的情况。这个格子的情况会不会被最终解引用。这个元素在最终解中到底会不会被加入。动态规划是不知道的。也不关心。

动态规划的出表后所得到的只是能获得的最大价值。并不是选择方式。然而选择方式可以通过价值表倒推得到。

0   0   0   0   0   0   0   0   0   
0   0   0   100 100 100 100 100 100 
0   0   0   100 100 120 120 120 220 
0   0   0   100 100 120 120 180 220 
0   50  50  100 150 150 170 180 230 

先查看右下角格子。跟它上方的格子比较,如果比上方的大。意味着:背包容量为8.有D物品比没有D物品的最大价值要大。也就是背包容量为8,有D物品的情况下,D物品被放入了。

然后我们把D物品(D 1h 50€)的空间/重量减掉。剩余容量7。不包含D物品。查看C行,第7列。是180,大于它上方的120.同理说明C也被选中了。

把C 4h 80€的重量减掉,剩余容量3.查看B行,第3列。是100。和它上方的100(A,3)相同。故B没有被选入。

这时查看(A,3)100上方的格子,(因为B未选中,我们就可以移动到不包含B的情况了。)。是0.小于100.故A被选中了。

Written with StackEdit.

4月 11, 2014

Quadcopter build log

四轴[四旋翼]飞行器搭建日志

和小伙伴@施闻轩上了一个名为"微小飞行器制作"的课。本以为是教做四轴飞行器的,后来发现主要是做航模的,不过老师也鼓励我们做四轴。

配置表

物品 名称 单重 总重 个数 单价 总价
电机 郎宇 A2212 kv980 56 224 4 63 252
电池 狮牌3S 5200mah30C 380 760 2 180 360
电调 4X 好盈 天行者 20A 37 148 4 48 192
机架 四轴飞行器450机架 282 282 1 55 55
螺旋浆 4X 10*47 0 2 34.65 69.3
充电器 1 38 38
各类接线、接头 3.5mm香蕉头 5 1.1 5.5
XT60 并联头 1 8 8
XT60 公头线 1 5 5
链接线扎带充电器等 1414 1130.8

以上价格都没包括邮费。淘宝链接我就不给了。也不敢说自己买到得是比较好的。

第一批配件,后面其实充电器、接线都是后来买的。目前买了三批: 配件

然后买的电机的香蕉头是没焊上的。要自己焊。也有些是焊好的。其实没焊上去我们可以调整线的长度啥的。我当时没多想就急着焊上去了。_(:з」∠)_。然后电调一般是没焊的。焊接的时候固定好香蕉头,把焊锡融在槽里放满。在冷却前把电线铁芯塞进去等凝固就行了。不用把电线包皮也沾住。然后用热缩管套上热风枪或者电吹风吹一吹缩好就行了。上图: 香蕉头焊接2 寝室没台钳,用院里发的神书《软件精英》[从没看过]凑合。然后电烙铁也是个很破的。工作室的高级的头很细。能从香蕉头母头边缘的小孔插进去。很是方便。 香蕉头焊接 谁公头谁母头其实随意。但似乎惯例是电机公头。电调母头。 电调电机香蕉头 电机和电调能链接后就可以拿Arduino控制电调(ESC)试试电机了。 Arduino Uno Arduino IDE非常好用 Arduino的IDE非常友好。插上。File--Examples里面例子很全。而且十分顺利地正常运行。 Arduino控制ESC 实例 事实上电调(ESC)只需要PWM脉冲信号就够了。而Arduino的Servo库所封装的信号正式PWM脉冲信号。所以就可以用Servo封装好的对象就可以了。只要将电调(ESC)地线(GND)接到Arduino Digital的GND。ESC GND另一外侧的白线(我的是白色,看说明书,反正也只有三根)接一个板上带~符号的PWM的Digital接口接线就完成了。电调控制线剩下的中间一根据说是提供5V的电源。没试,不确定。 arduino esc首次转动 接线简单但由于对电调接受信号不了解还是折腾了好一会才让电机转了起来。一直盯着电调的说明书看。但说明书上没跟我说解锁(Arm)的概念。简单说来就是电调不是motor.write(0);输出0信号就是静止的。而是有个最小量程。并且不为0.因为0信号和没接线没信号是一样的。一般motor.write(40); 40就可以Arm了。也就是让电调知道自己能正常接收命令了。然后设置油门最小最大量程什么的比较烦。建议用串口控制Arduino发送PWM来模拟遥控器(或者你有遥控器接好也行)来对电调进行设置(参考说明书好盈天行者20A手册) 这也是个神烦的事情。我一度想给电调刷BLHeli固件。但发觉不刷也能用就没冒这个风险了。(主要对焊工不自信,怕焊坏电调) 喔。忘说另一个坑点。电调的信号接线是三根一起的杜邦母线。Arduino Uno上的接口。大部分全特么是母接口(孔)。而且Digital这边的GND只有一个。(我们后来买了块扩展板。扩展出多个GND VCC digital analog的接口。方便接线。) 目前我是这么做的: 把杜邦壳拆了。插根掰直的曲排针,你有指派针或者其他跳线之类就直接接上去就行了。 拆杜邦壳 掰直的曲排针

看了很多关于卡尔曼滤波的东西。Youtube上的教程比较易懂。但也只是对其大概思路和用处有了了解。下面图形是用别人的库画出来的。Arduino代码写入到Arduino。Processing代码需要用Processing运行。

卡尔曼滤波

买了一堆针对针、孔对孔、孔对针的杜邦线以及传感器扩展板后。总算不用蛋疼的拆开杜邦壳接线了。于是我又把电调的杜邦壳给装了回去。 又一个坑点,Arduino板和机架上的孔位完全对不上。。我们就把保护盖装上用扎带把Arduino绑上去了。。

扩展板

上周(2014-4-18)拿到场外测试了下电机和电池。用一块电池供两个电机运转。调到能刚好带起机架以及一块电池的功率。然后石头什么的压住看能运转多久。最后测试下来是28分钟。考虑到功率调的略高。估计下来空载是能够超过30分钟。但载重1kg可能要20到30分钟了。

场外动力测试

这周(2014-4-25)新加入的小伙伴@袁炜鸿第一次过来一起做。本来打算把MPU 6050接上调PID的。。结果传感器扩展板接上去再接MPU的时候正负接反。一缕青烟冒出。MPU烧坏了。好在MPU也不贵。正好我们换mpu 9150。除了加速度陀螺仪还有磁感(电子罗盘)。诡异的是。。mpu 9150价格到了7、80.mpu 6050只有十块不到。磁感这么贵?

Written with StackEdit.

Next → Page 1 of 2