• 一图流解释 Alpha-Beta 剪枝(Alpha-Beta Pruning)

    Alpha-Beta剪枝用于裁剪搜索树中不需要搜索的树枝,以提高运算速度。它基本的原理是:

    • 当一个 Min 节点的 β值≤任何一个父节点的α值时 ,剪掉该节点的所有子节点
    • 当一个 Max 节点的 α值≥任何一个父节点的β值时 ,剪掉该节点的所有子节点

    下面为只使用 MiniMax 和使用 Alpha-Beta 剪枝的简单对比。

    MiniMax search without alpha-beta pruning
    MiniMax search with alpha-beta pruning

    需要注意的是,剪枝的效果与树节点的访问顺序有关。

    Alpha-Beta剪枝的伪代码如下:

    Initialize MAX_VALUE(node,game,-∞,∞)
    
    function MAX_VALUE(state,game,α,β) returns the minimax value of state
        inputs: state, current state in game
                game, game description
                α, the best score for MAX along the path to state
                β, the best score for MIN along the path to state
    
        if CUTOFF_TEST(state) then return EVAL(state)
        for each s in SUCCESSORS(state) do
            α = MAX(α, MIN_VALUE(s,game,α,β))
            if α ≥ β then return β
        end
        return α
    
    function MIN_VALUE(state,game,α,β) returns the minimax value of state
        if CUTOFF-TEST(state) then return EVAL(state)
        for each s in SUCCESSORS(state) do
            β = MIN(β, MAX_VALUE(s,game,α,β))
            if α ≥ β then return α
        end
        return β

    下面用一个例子说明。规定从左节点开始展开。原搜索树为:

    阅读更多…
  • 使用 Python OpenCV 3.4.x 进行 SIFT 特征检测与匹配

    前言

    Harris算法和Shi-Tomasi 算法,由于算法原理,具有旋转不变性,在目标图片发生旋转时依然能够获得相同的角点。但是如果对图像进行缩放以后,再使用之前的算法就会检测不出来,如图:

    图像放大,窗口不变,导致检测结果发生变化

    在2004年,University of British Columbia 的 D.Lowe 在他的论文 Distinctive Image Features from Scale-Invariant Keypoints 中提出了一个新的算法,Scale Invariant Feature Transform (简称SIFT),它可以提取关键点及计算其描述符。OpenCV的文档指出这篇论文容易理解,推荐阅读。

    SIFT算法主要有4个步骤,详情请见文末的相关参考。

    流程

    1. 尺度空间极值检测(Scale-space Extrema Detection)
    2. 关键点定位(Keypoint Localization)
    3. 方向分配(Orientation Assignment)
    4. 关键点描述符(Keypoint Descriptor)
    阅读更多…
  • 霍夫变换(Hough Transform)

    简介

    霍夫变换(Hough Transform)最初用于检测图像中的直线或者圆等几何图形,主要应用在图像分析、计算机视觉和数字图像处理领域。后来经过拓展,可适用于任意图形的检测,及一些参数取值的检测。

    霍夫直线变换的基本思想

    如果两点 (xi, yi) 和 (xj, yj) 都在一条直线上,那么它们在x-y平面上有相同的斜率和y轴的截距。

    对于一个点 (xi, yi) ,经过直线 yi = axi + b,其中a为斜率,b为y轴截距。可以把该式改写为 b = (-xi)a+ yi ,使a为自变量,b为因变量,a可取[amin, amax],代入a可求出对应的b值。a和b的关系可以在参数空间(即a-b平面)上作图。把参数空间分隔为一个一个格子(累加器),然后把 (a, b) 对应的格子A(a, b) 加 1。
    也就是说,一个点可以使参数空间的一系列累加器都加 1。

    对于另外一个点 (xj, yj) ,把 yj = axj + b 改写为 b = (-xj)a+ yj ,作同样的操作,对应的一系列累加器加1。

    阅读更多…
  • 极路由如何走到濒临破产:沿着悬崖奔跑 辉煌与沉寂后的思考

    转载自:https://www.cnbeta.com/articles/tech/753389.htm

    这些年智能硬件单品层出不穷,一些被上帝眷顾的技术大佬偶尔会因为一些生活中遇到的小问题,一言不合,就创造出一个属于自己的时代符号,极路由创始人王楚云就是这样一个人,而他选中的是智能路由器。

    2011年,王楚云在做微博营销,当时他经常被公司的宽带网络“贵”、“慢”两大问题所困扰,为解决这一问题,尝试过各种办法无果后,想到了“改装”路由器——在一台有USB口的路由器上刷Openwrt,将三大运营商的网络做负载均衡,新网络带宽神奇地从原来的1M变成了10M,而网费却由原来的1000元变成了600元,正是这一次的魔术手使得王楚云走上了他的创业之路。

    创业最艰难的就是构建核心团队,最初王楚云找来了大街网的前同事李恺,同为技术出身的李恺自然与其一拍即合,接着是曾在TP-Link带队研发过红极一时的WR-703N的张利鹏的加入,使得核心团队基本成型。至此,极路由三驾马车已到齐,产品研发开始进入正轨,而摆在未来的却是未知的考验。

    阅读更多…

  • Python 的 __new__() 方法与实践

    本文主要转自:http://www.cnblogs.com/ifantastic/p/3175735.html

    __new__() 是在新式类中新出现的方法,它作用在构造方法建造实例之前,可以这么理解,在 Python 中存在于类里面的构造方法 __init__() 负责将类的实例化,而在 __init__() 启动之前,__new__() 决定是否要使用该 __init__() 方法,因为__new__() 可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例

    如果将类比喻为工厂,那么__init__()方法则是该工厂的生产工人,__init__()方法接受的初始化参数则是生产所需原料,__init__()方法会按照方法中的语句负责将原料加工成实例以供工厂出货。而__new__()则是生产部经理,__new__()方法可以决定是否将原料提供给该生产部工人,同时它还决定着出货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品。

    __new__() 方法的特性:

    • __new__() 方法是在类准备将自身实例化时调用
    • __new__() 方法始终都是类的静态方法,即使没有被加上静态方法装饰器

    类的实例化和它的构造方法通常都是这个样子:

    class MyClass(object):
        def __init__(self, *args, **kwargs):
            pass
    
    myclass = MyClass(*args, **kwargs)  # 实例化

    正如以上所示,一个类可以有多个位置参数和多个命名参数,而在实例化开始之后,在调用 __init__() 方法之前,Python 首先调用 __new__() 方法:

    def __new__(cls, *args, **kwargs):
        pass

    第一个参数cls是当前正在实例化的类。

    如果要得到当前类的实例,应当在当前类中的 __new__() 方法语句中调用当前类的父类的 __new__() 方法。例如,如果当前类是直接继承自 object,那当前类的 __new__() 方法返回的对象应该为:

    def __new__(cls, *args, **kwargs):
        ...
        return object.__new__(cls, *args, **kwargs)  # Python 2 是这样的,但这不适用于Python 3,会报错,后文不重复提示这点

    注意:
          事实上如果(新式)类中没有重写__new__()方法,即在定义新式类时没有重新定义__new__()时,Python默认调用该类的直接父类的__new__()方法来构造该类的实例,如果该类的父类也没有重写__new__(),那么将一直按此规矩追溯至object的__new__()方法,因为object是所有新式类的基类。
          而如果新式类中重写了__new__()方法,那么你可以自由选择任意一个的其他的新式类(必定要是新式类,只有新式类必定都有__new__(),因为所有新式类都是object的后代,而经典类则没有__new__()方法)的__new__()方法来制造实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死循环。具体看以下代码解释:

    class Foo(object):
        def __init__(self, *args, **kwargs):
            ...
        def __new__(cls, *args, **kwargs):
            return object.__new__(cls, *args, **kwargs)    
    
    # 以上return等同于 
    # return object.__new__(Foo, *args, **kwargs)
    # return Stranger.__new__(cls, *args, **kwargs)
    # return Child.__new__(cls, *args, **kwargs)
    
    class Child(Foo):
        def __new__(cls, *args, **kwargs):
            return object.__new__(cls, *args, **kwargs)
    
    class Stranger(object):  # 在制造Stranger实例时,会自动调用 object.__new__(cls)
        ...
    
    # 如果Child中没有定义__new__()方法,那么会自动调用其父类的__new__()方法来制造实例,即 Foo.__new__(cls, *args, **kwargs)。
    # 在任何新式类的__new__()方法,不能调用自身的__new__()来制造实例,因为这会造成死循环。因此必须避免类似以下的写法:
    # 在Foo中避免:return Foo.__new__(cls, *args, **kwargs) 或 return cls.__new__(cls, *args, **kwargs)。Child同理。
    # 使用object或者没有血缘关系的新式类的__new__()是安全的,但是如果是在有继承关系的两个类之间,应避免互调造成死循环,例如:(Foo)return Child.__new__(cls), (Child)return Foo.__new__(cls)。

    通常来说,新式类开始实例化时,__new__()方法会返回cls(cls指代当前类)的实例,然后该类的__init__()方法作为构造方法会接收这个实例(即self)作为自己的第一个参数,然后依次传入__new__()方法中接收的位置参数和命名参数。

    注意:如果__new__()没有返回cls(即当前类)的实例,那么当前类的__init__()方法是不会被调用的。如果__new__()返回其他类(新式类或经典类均可)的实例,那么只会调用被返回的那个类的构造方法【不包括__init__()方法】

    class Foo(object):
        def __init__(self, *args, **kwargs):
            ...
        def __new__(cls, *args, **kwargs):
            return object.__new__(Stranger, *args, **kwargs)  
    
    class Stranger(object):
        ...
    
    foo = Foo()
    print type(foo)    
    
    # 打印的结果显示foo其实是Stranger类的实例
    
    # 因此可以这么描述__new__()和__init__()的区别,在新式类中__new__()才是真正的实例化方法,为类提供外壳制造出实例框架,然后调用该框架内的构造方法__init__()使其丰满。
    # 如果以建房子做比喻,__new__()方法负责开发地皮,打下地基,并将原料存放在工地。而__init__()方法负责从工地取材料建造出地皮开发招标书中规定的大楼,__init__()负责大楼的细节设计,建造,装修使其可交付给客户。

    ================ 以上是转载的原文 =========================

    下面是我的一点使用经验。此前我也没具体使用过__new__()方法,只是大概听过有这么一个事。

    最近项目新增了一种网络协议,而API不变,希望同时适配不同的网络协议(数据包中有给出版本号可以判断)。然后就想到了在实例化的__new__()方法里面返回一个其他类的实例,进而拜读了原文……

     

    粗略阅读原文后,写出来了类似这样的代码:

    condition = None
    
    class A(object):
        def __new__(cls, *args, **kwargs):
            print('A new')
            if condition == 'A':
                return object.__new__(cls)  # cls == A
            elif condition == 'B':
                return object.__new__(B)
    
        def __init__(self, value):
            self.value = value
    
    
    class B(object):
        def __new__(cls, *args, **kwargs):
            print('B new')
            return object.__new__(B)
    
        def __init__(self, value):
            self.value = value
    
    condition = 'A'
    a = A(123)
    print(type(a))
    print(a.value)
    
    condition = 'B'
    b = A(456)
    print(type(b))
    print(b.value)

    运行结果为:

    A new
    <class '__main__.A'>
    123
    
    A new
    <class '__main__.B'>
    Traceback (most recent call last):
      File "/xxxxx/main.py", line 31, in <module>
        print(b.value)
    AttributeError: 'B' object has no attribute 'value'

    A这块是正常的,而B这块就很不正常了:没有value这个属性不说,print(‘B new’) 都没有执行!
    ——这是 return object.__new__(B) 导致的,因为object的__new__() 方法没有 print(‘B new’) 这句。

    我们把 return object.__new__(B) 改成 return B.__new__(cls) ,像这样:

    class A(object):
        def __new__(cls, *args, **kwargs):
            print('A new')
            if condition == 'A':
                return object.__new__(cls)  # cls == A
            elif condition == 'B':
                return B.__new__(cls)

    这下执行结果有显示 B new 了。然而,’B’ object has no attribute ‘value’,还是没有。
    把 return B.__new__(cls) 改为 return B.__new__(cls, *args, **kwargs) ,把参数传进去,结果还是一样。

    难道是B类的问题?把它改成:

    class B(object):
        def __new__(cls, *args, **kwargs):
            print('B new')
            return object.__new__(B, *args, **kwargs)

    依旧报错(在Python 3下还会报 TypeError: object() takes no parameters)!有点抓狂了,再去精读一下,发现了上面被我标红的一句“如果__new__()返回其他类(新式类或经典类均可)的实例,那么只会调用被返回的那个类的构造方法”。就是说,__init__()方法没有被调用啊!我来手动调用一下:

    condition = None
    
    class A(object):
        def __new__(cls, *args, **kwargs):
            print('A new')
            if condition == 'A':
                return object.__new__(cls)  # cls == A
            elif condition == 'B':
                return B.__new__(cls, *args, **kwargs)
    
        def __init__(self, value):
            self.value = value
    
    
    class B(object):
        def __new__(cls, *args, **kwargs):
            print('B new')
            self = object.__new__(B)  # 创建对象
            self.__init__(*args, **kwargs)  # 初始化对象
            return self  # 初始化之后再返回结果
    
        def __init__(self, value):
            self.value = value
    
    condition = 'A'
    a = A(123)
    print(type(a))
    print(a.value)
    
    condition = 'B'
    b = A(456)
    print(type(b))
    print(b.value)

    这次终于和期望一致了……

     

    总结:

    1. 如果__new__()返回其他类的实例,那么只会调用被返回的那个类的构造方法【不包括__init__()方法】;
    2. 如果__new__()返回当前类的实例,可以直接 return object.__new__(cls) ,不需要手动传参数;若返回其他类的参数,需 return ClassName.__new__(cls, *args, **kwargs) 手动传参数,万万不能 return object.__new__(ClassName, *args, **kwargs) 。
  • 游戏策划:为什么我的儿子不沉迷游戏?

    今天是高考第一天,越来越多的家长问我孩子网瘾的问题。

    我作为一名从业多年的资深游戏策划,首先得说个现实,商业化的网络游戏,无不是为让玩家沉迷所设计的。

    为了让玩家沉迷,我们做的功课比各位父母要深入的多,这根本不是一个维度的对抗,所以无奈是大多数父母的感受。

    我们非常清楚你的儿子想要什么,愿意付出什么,以及什么是他喜欢的。百万玩家的数据和调研在我们的数据平台上随时可查,我们的每一个改动都和数据有关,我相信我比你更了解你的儿子的喜好。

    作为一个游戏策划,这是我的工作,但作为一个家长,我儿子也读小学了,但他却没有对游戏成瘾。

    说句不好听的,引导玩家和教育儿子没有本质的区别,而在引导玩家这件事上,我经验丰富。

    阅读更多…

  • 十年经历第7次易主 有了钱和技术的A站会回春吗

    动荡不安的A站终于迎来了第7次易主。在抛来橄榄枝的阿里、今日头条、快手三家中,经过半年多的复杂谈判与斡旋,A站最终选择被快手全资收购。这也意味着,A站将打破股东及管理层混乱局面:结束奥飞系的蔡东青、软银中国、优酷土豆和中文在线四大股东的复杂关系,拥有一家普通公司该拥有的稳定运营管理结构。

    文/彭丽慧网易科技

    未来,A站将保持独立品牌、维持独立运营、保持原有团队、独立发展。而快手也会在资金、资源、技术等给予A站大力支持。那么,还想再活五百年的A站真能如所愿吗?

    阅读更多…

  • 搭建 git 本地中转站

    局域网内有多台开发机器,因为种种原因,与服务器同步代码有不便之处。于是打算在本地做一个 git 的镜像,所有机器都统一 clone 这个本地镜像库,然后由这个镜像库负责与服务器更新。

    1. 使用 –mirror 参数 clone

    cd /some/where/
    git clone --mirror git@server.com:user/someproject.git

    执行以上命令后,在本地的 /some/where/someproject.git/ 下建立了对应项目的镜像,它是一个裸版本库(不包含工作区,直接就是版本库的内容),对于我这样的新手来说不是很好懂什么是“裸版本库”,但是进去目录看一下就知道了。

    2. 本地操作

    在同一台机器上,我们这样写代码:

    cd ~/workspace/
    git clone /some/where/someproject.git

    这样 clone 出来的就是平时熟悉的、包含工作区的内容,平时怎么用就怎么用。
    阅读更多…

  • 申请 Let’s Encrypt 通配符 HTTPS 证书,并配置 Apache2

    3月中旬, Let’s Encrypt 终于正式发布通配符 HTTPS 证书了,赶紧去申请一个玩玩(然而我的网站暂时还不支持加SSL,只能先在VPS上试试了 /(ㄒoㄒ)/)。

    1. 安装 Let’s Encrypt 客户端

    系统为 Ubuntu 16.04,参照 Certbot 官网的教程,运行以下命令安装(我这里由于是用 root 用户,所以非 root 请自行加 sudo):

    # apt-get update
    # apt-get install software-properties-common
    # add-apt-repository ppa:certbot/certbot
    # apt-get update
    # apt-get install python-certbot-apache

    2. 获取通配符证书

    先查看 certbot 的版本是否 > 0.22,否则不支持通配符证书:

    # certbot --version
    certbot 0.22.2
    

    自带的 –apache 模式似乎并不能处理通配符证书的情况,所以需要手动获取。

    在对应的 support 页面[1]中,了解到还需要手动指定服务器,执行:

    # certbot -d *.xxxx.com --manual --preferred-challenges dns-01 --server https://acme-v02.api.letsencrypt.org/directory certonly

    之后按提示操作,并添加自己的域名的 TXT 记录,以通过 ACME 认证。完成后,可在 /etc/letsencrypt/live/xxxx.com/ 下看到几个 .pem 文件。

    阅读更多…