Quantcast
Channel: CSDN博客推荐文章
Viewing all 35570 articles
Browse latest View live

关于混合应用开发的未来的一些思考

$
0
0

当其欣于所遇,暂得于己,快然自足,不知老之将至,及其所之既倦,情随事迁,感慨系之矣

有序而又混乱的时代

现在是2017年11月9日。即使努力仅仅去关注编程技术的我,也依然感觉到了混乱。混乱之后,必然潜伏着机遇;混乱之后,也可能一着错,几年辛苦付诸流水。

几年之前,我偏执的认为没有开源精神的微软,不值得自己追随,毅然决然地完全摒弃了 .net 系的学习之后。现在单一 .net 技术栈的人,可能工作都很难找到了吧。

但是,没有谁会一直有好运气。说不定,我现在沉浸的语言,不久之后就成了明日黄花。如果自己真的是只会某个语言的专家,真的就是要一切从零开始了。你的所有荣誉,都被清零。纵然可以自我安慰说一些有基础学其他的也很快一类的话。

如此,或许能理解我现在为何付出极大的时间和精力,同时并行推进多个语言的学习和实践了。

当然,现在(估计未来也是)还是一个技术软文丛生的时代。准确分辨出软文和趋势,真的需要些许智慧了。

对一些编程语言的观察和思考

1. C/C++ 永生

过去几十年的编程语言之争,C/C++ 似乎一直置身事外,但又以无可争议的方式讲述着自己的威严。不同的语言,大都将能兼容 C/C++ 库,作为语言本身必须的一个 feature 实现。感觉自己那本 Boost 库书,该燥起来了!

2. JVM 系会继续活的很好

原来一直以为 jvm 系的语言,普遍都很慢;但是看了 go 相关的评测后,才发现在大多数情况下,因为有一个优化的很好的解释器, jvm 系的效率是非常高的。路转粉。Java 是 JVM 的亲儿子,但是我现在在看 Kotlin,呼哈哈~~

3. Python 可能是最合适的贴身脚本语言

目前技术栈中,真正充当日常脚本语言的是 nodejs。正在逐步往 Python 方向过渡。暂且不论大数据,人工智能一类的场景,对Python的良好支持;单是 Mac 和 Linux 系统内置 Python 支持者一向,都已经决定了 Python 几乎无可撼动的江湖地位。

4. JavaScript 可能不是跨平台应用开发的良好选择

在我看来 Virtual DOM 真正的价值从来都不是性能,而是它 1) 为函数式的 UI 编程方式打开了大门;2) 可以渲染到 DOM 以外的 backend,比如 ReactNative。

根据 @尤雨溪 的分析,未来的设计到 UI 呈现的应用开发,Virtual Dom是一个很重要的趋势。对此,我也很认同。毕竟有一个目前为止取得空前成功的 ReactNative 背书。但是,我无法认同,使用 JavaScript 作为中间语言来进程跨平台开发。我在想,如果不是 ReactJS 的历史原因,有没有可能, Facebook 会专门为 ReactNative 用其他语言写一套类似 ReactJS 的库。C/C++ 实现跨平台开发,我总觉得,它会是一个更好的选择。

5. 未来的应用,会更加追求品质和体验

这一点纯属吐槽吧。对于那些很 LOW 的客户端,我是很拒绝打开的。真的看着糟心,还不如看网页。未来,随着技术的进步,土豪的增多,大家在功能之外,应该会更加注重体验和品质吧。反正我是觉得,一些签到好处的动画,是非常酷的。所以,有事没事读读 Material Design,也是极好的

小福利

如果有研究 iOS/Android 如何和 WebView 同步异步通信的,可以看下这两个库: https://github.com/wendux/DSBridge-IOShttps://github.com/wendux/DSBridge-Android 。本来想自己写下相关的文章的,但是既然已经有童鞋封装好了,也不用再重复说了,方法就是网上常讨论的那些同步异步通信策略,源码值得一看。

参考文章

作者:sinat_30800357 发表于2017/11/9 1:34:15 原文链接
阅读:39 评论:0 查看评论

JDK 动态代理运行原理

$
0
0

JDK 动态代理运行原理


    1. 程序演示
    1. 源码讲解
    1. 总结

这几天有空研究了下JDk的动态代理,JDK的动态代理类都在java.lang.reflect包下,写了一些小程序来演示了相关类的使用,同时做了一些与CGLIb的对比,以后有空再讲述下lombok中相关注解的使用。

1. 程序演示


接口:HelloWorld:

public interface HelloWorld {

    void sayHello();
}

对应的实现类为:
HelloWorldImpl:

public class HelloWorldImpl implements HelloWorld {
    @Override
    public void sayHello() {
        System.out.println("Hello world");
    }
}

接口与对应的实现的逻辑是比较简单的,在这只是讲述JDK动态代理的原理,业务逻辑也无需要复杂的业务逻辑。

代理类MyInvocationHandler:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * Created by xiaxuan on 17/11/7.
 */
public class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before method invoke : " + method.getName());
        return method.invoke(target, args);
    }
}

测试类TestProxy:

import java.lang.reflect.Proxy;

/**
 * Created by xiaxuan on 17/11/7.
 */
public class TestProxy {

    public static void main(String[] args) {
        HelloWorld hw = (HelloWorld) Proxy.newProxyInstance(HelloWorld.class.getClassLoader(), new Class[] {HelloWorld.class}, new MyInvocationHandler(new HelloWorldImpl()));
        hw.sayHello();

    }

}

运行结果为:

使用上其实还是挺简单的,以下是关键的动态代理的源码讲解。

2. 源码讲解


我们先进入Proxy.newProxyInstance()中查看,如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        ......
        }
    }

我省略了后面的代码,上面的代码中关键的一行为

getProxyClass0(loader, intfs);

转到对应的方法为:

    /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

从proxyClassCache中取出class,进入到get方法中,如下:

    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

这里面关键的代码是以下两行:

Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);

这里真正生成代理类的源码为 subKeyFactory.apply(key, parameter)
Supplier<V> supplier = valuesMap.get(subKey),这行代码中的Supplier对象并不是在运行到这的时候就能取到,而是在使用当前Supplier对象的时候才会实例化出来,这个是java8中的一个延迟加载的新特性。

进到方法subKeyFactory.apply(key, parameter)中,查看代码:

“`
public Class

总结


动态代理,在第一次生成的对应的代理对象后,将其存在缓存中,然后再次调用的时候就直接从缓存中取出代理对象,然后调用对应的代理方法实现需要的效果。

在此就需要提下JDK这种动态代理和CGLIB这种的区别了, CGLIB一般是在编译阶段对生成的class进行替换,在实际运行的时候不需要再去生成字节码替换调用了,而JDK动态代理的话,在运行阶段生成代理类进行调用一般来说会稍微慢一些。

以后有空讲讲lombok中的@DATA注解的用法和原理,就是使用ASM在源码编译阶段生成class进行替换,相对于JDK动态代理来说速度要快许多。

作者:u012734441 发表于2017/11/9 7:58:01 原文链接
阅读:45 评论:0 查看评论

深度探索C++ 对象模型【第五章1】

$
0
0

1:拥有纯虚函数的基类不可能拥有实例对象。virtual =0


2:一般而言,class 的data member应该被初始化,而且只能以两种方式初始化

  • 在constructor中指定初值
  • 在member function 中指定初值
任何其他的初始化操作都会破坏封装的特性,使得class的维护和修改变得困难。

3:C++中,纯虚函数是可以定义和调用的,但是其只能被静态的调用,不能经由虚拟机制调用。

4:一个比较好的建议:不要讲虚析构函数定义为纯虚函数,定义为虚函数是比较好的~。

5:不将函数声明为const,意味着此函数不能获得一个const的指针或引用。但是这也就意味这该函数没有修改任何一个data member。有舍有得。

6:一般来说,对象有三种产生方式
  • global内存配置,全局对象的生命周期是和整个程序的生命周期相同的
  • Local内存配置,局部对象的生命周期是程序结束之前析构函数调用时结束的
  • heap内存配置(new出来的对象),堆上的对象的生命周期是从new到delete的
7:对于全局对象,其构造函数会在程序的起始处被调用,在程序exit()处调用其析构函数(exit()函数是由系统产生的,放在main函数结束之前)。注意:实际上,就想第三第四章所说的那样,有些类的没有必要的构造函数和析构函数要不是在实际情况下没有被定义出来,要不就是没有被调用。

8:在C++中,全局对象被视为完全定义,C++的所有全局对象都被以“初始化过的数据”来对待

9:C++的类中如果只定义了数据,那么会被以Plain Ol data 来对待,也就是纯数据模式,这种情况下既没有析构函数也没有构造函数、拷贝构造函数的定义与调用。

10:当C++的类中出现了显式的构造函数,有public接口和private接口后,其大小并没有实际的改变(因为其没有虚函数机制)

11:显式的初始化列表方式会比其他的成员数据初始化方式效率更高。这是因为当函数的激活记录被放入程序堆栈时,上述的初始化列表就可以被放进对象的内存中去了。

12:显式的初始化列表带来了三个明显的缺点
  • 只有当数据成员为public时,显式初始化列表才会比较的高效率
  • 只能指定常量,因为它们在编译器就可以被评估求值
  • 由于编译器并没有自动施行,所以初始化行为的失败可能性会变高
对于显式成员初始化列表的效率优点来说,平衡上面的软件工程上的缺点是不可能的。只有在四种特殊情况下必须使用初始化列表时,或者data member数据结构巨大(调色板数据、OGRE顶点数据)时,可以选择使用。此时显式的初始化列表比inline的构造函数效率还要高,特别是对全局对象来说。

13:无用的构造函数、拷贝构造函数、析构函数,实际上编译器并没有产生它们。

14:当C++的类出现继承性质时,虚函数的导入,理论上虚析构函数的定义会带来好处~

15:虚函数的出现,由于虚函数表,每一个对象都要付出vptr的代价(32位机器为4字节),如若是计算机图形学中的顶点,这样的负担就有点大了,所以在设计类的时候,需要统筹考虑到时间空间的负担和虚函数带来的弹性之间的优劣。

作者:misayaaaaa 发表于2017/11/9 9:11:43 原文链接
阅读:35 评论:0 查看评论

Python基础入门之Dict和Set类型二

$
0
0

4.6、Python中什么是set


dict的作用是建立一组 key 和一组 value 的映射关系,dict的key是不能重复的。

有的时候,我们只想要 dict 的 key,不关心 key 对应的 value,目的就是保证这个集合的元素不会重复,这时,set就派上用场了。

set 持有一系列元素,这一点和 list 很像,但是set的元素没有重复,而且是无序的,这点和 dict 的 key很像。

创建 set 的方式是调用 set() 并传入一个 list,list的元素将作为set的元素:

>>> s = set(['A', 'B', 'C'])

可以查看 set 的内容:

>>> print s

set(['A', 'C', 'B'])

请注意,上述打印的形式类似 list, 但它不是 list,仔细看还可以发现,打印的顺序和原始 list 的顺序有可能是不同的,因为set内部存储的元素是无序的。

因为set不能包含重复的元素,所以,当我们传入包含重复元素的 list 会怎么样呢?

>>> s = set(['A', 'B', 'C', 'C'])

>>> print s

set(['A', 'C', 'B'])

>>> len(s)

3

结果显示,set会自动去掉重复的元素,原来的list有4个元素,但set只有3个元素。

任务6

请用set表示班里的4位同学:

Adam, Lisa, Bart, Paul


4.7Python之访问set


由于set存储的是无序集合,所以我们没法通过索引来访问。

访问 set中的某个元素实际上就是判断一个元素是否在set中。

例如,存储了班里同学名字的set:

>>> s = set(['Adam', 'Lisa', 'Bart', 'Paul'])

我们可以用 in 操作符判断:

Bart是该班的同学吗?

>>> 'Bart' in s

True

Bill是该班的同学吗?

>>> 'Bill' in s

False

bart是该班的同学吗?

>>> 'bart' in s

False

看来大小写很重要,'Bart' 和 'bart'被认为是两个不同的元素。

任务7

由于上述set不能识别小写的名字,请改进set,使得 'adam' 和 'bart'都能返回True。


4.8Pythonset的特点


set的内部结构和dict很像,唯一区别是不存储value,因此,判断一个元素是否在set中速度很快。

set存储的元素和dict的key类似,必须是不变对象,因此,任何可变对象是不能放入set中的。

最后,set存储的元素也是没有顺序的。

set的这些特点,可以应用在哪些地方呢?

星期一到星期日可以用字符串'MON', 'TUE', ... 'SUN'表示。

假设我们让用户输入星期一至星期日的某天,如何判断用户的输入是否是一个有效的星期呢?

可以用 if 语句判断,但这样做非常繁琐:

x = '???' # 用户输入的字符串

if x!= 'MON' and x!= 'TUE' and x!= 'WED' ... and x!= 'SUN':

    print 'input error'

else:

    print 'input ok'

注意:if 语句中的...表示没有列出的其它星期名称,测试时,请输入完整。

如果事先创建好一个set,包含'MON' ~ 'SUN':

weekdays = set(['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'])

再判断输入是否有效,只需要判断该字符串是否在set中:

x = '???' # 用户输入的字符串

if x in weekdays:

    print 'input ok'

else:

    print 'input error'

这样一来,代码就简单多了。

任务8

月份也可以用set表示,请设计一个set并判断用户输入的月份是否有效。

月份可以用字符串'Jan', 'Feb', ...表示。


4.9Python之遍历set


由于 set 也是一个集合,所以,遍历 set 和遍历 list 类似,都可以通过 for 循环实现。

直接使用 for 循环可以遍历 set 的元素:

>>> s = set(['Adam', 'Lisa', 'Bart'])

>>> for name in s:

...     print name

... 

Lisa

Adam

Bart

注意: 观察 for 循环在遍历set时,元素的顺序和list的顺序很可能是不同的,而且不同的机器上运行的结果也可能不同。

任务9

请用 for 循环遍历如下的set,打印出 name: score 来。

s = set([('Adam', 95), ('Lisa', 85), ('Bart', 59)])


4.10Python之更新set


由于set存储的是一组不重复的无序元素,因此,更新set主要做两件事:

一是把新的元素添加到set中,二是把已有元素从set中删除。

添加元素时,用set的add()方法:

>>> s = set([1, 2, 3])

>>> s.add(4)

>>> print s

set([1, 2, 3, 4])

如果添加的元素已经存在于set中,add()不会报错,但是不会加进去了:

>>> s = set([1, 2, 3])

>>> s.add(3)

>>> print s

set([1, 2, 3])

删除set中的元素时,用set的remove()方法:

>>> s = set([1, 2, 3, 4])

>>> s.remove(4)

>>> print s

set([1, 2, 3])

如果删除的元素不存在set中,remove()会报错:

>>> s = set([1, 2, 3])

>>> s.remove(4)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

KeyError: 4

所以用add()可以直接添加,而remove()前需要判断。

if 4 in s:

    s.remove(4)

任务10

针对下面的set,给定一个list,对list中的每一个元素,如果在set中,就将其删除,如果不在set中,就添加进去。

s = set(['Adam', 'Lisa', 'Paul'])

L = ['Adam', 'Lisa', 'Bart', 'Paul']


任务答案:

任务6:

s = set(['Adam','Lisa','Bart','Paul'])


任务7:

s = set(['Adam','Lisa','Bart','Paul','adam','bart','lisa','paul'])

print 'adam' in s

print 'bart' in s


任务8:

months = set(['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'])

x1 = 'Feb'

x2 = 'Sun'


if x1 in months:

    print 'x1: ok'

else:

    print 'x1: error'


if x2 in months:

    print 'x2: ok'

else:

    print 'x2: error'


任务9:

s = set([('Adam', 95), ('Lisa', 85), ('Bart', 59)])

for x in s:

    print x[0]+':',x[1]


任务10:

s = set(['Adam', 'Lisa', 'Paul'])

L = ['Adam', 'Lisa', 'Bart', 'Paul']

for name in L:

    if name in s:

        s.remove(name)

    else:

        s.add(name)

print s




作者:hbblzjy 发表于2017/11/9 9:22:32 原文链接
阅读:36 评论:0 查看评论

【比特币】BIP 0015 详细说明

$
0
0

BIP 0015 详细说明


  BIP: 15
  Layer: Applications
  Title: 别名
  Author: Amir Taaki <genjix@riseup.net>
  Comments-Summary: No comments yet.
  Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0015
  Status: Deferred
  Type: Standards Track
  Created: 2011-12-10

BIP 0070(付款协议)可以被视为替代别名。

使用香草比特币发送资金到目的地,需要一个1Hd44nkJfNAcPJeZyrGC5sKJS1TzgmCTjjZ的地址。 使用地址的问题是他们不容易记住。 如果域名不存在,则需要输入他们喜欢的网站的IP地址。

这个文件的目的是通过仔细的参数来设计一个比特币别名系统。 这是对协议的一个很大的修改,在将来不容易改变,并且有很大的影响。 有第一次纠正的动力。 别名必须是稳健的,安全的。

计划

这里有一些不同的建议和各系统的性能。

FirstBits

FirstBits是将区块链用作地址簿的建议。

当比特币被发送到地址时,该地址就会被记录在区块链中。 因此,知道这个地址存在或确实存在,只要看到有一个付款的地址。 FirstBits是一个具有令人难忘的别名的方法。 首先将地址转换为小写,然后取前几个唯一字符。 这是你的FirstBits别名。

举个例子,布拉格的brmlab hackerspace有一个购买食物或饮料或捐款的地址:

1BRMLAB7nryYgFGrG8x9SYaokb8r2ZwAsX

他们的FirstBits别名变成:

1brmlab

这是足够的信息被给予FirstBits别名1brmlab。 当有人想要购买时,如果没有FirstBits,他们要么手工输入他们的地址,扫描他们的二维码(这需要一个手机,这个作者不拥有),或在互联网上找到他们的地址复制和 粘贴到客户端发送比特币。 FirstBits通过提供简单的付款方式来缓解这种不切实际的情况。

与Vanitygen(虚荣发生器)一起,可以创建令人难忘的唯一命名地址。 地址是有意义的,而不是一个奇怪的字母和数字组合,但添加上下文到目的地。

然而FirstBits有它自己的问题。 一个是可能产生的别名受可用计算能力的限制。 生成一个完整或精确的别名可能是不可行的 - 只有近似值才有可能。 这也是计算资源密集型的,这意味着未来产生独特的别名需要大量的能量消耗,并且在普适计算的环境中不能扩展到家中的个人或手持设备的参与者的水平。

随着网络的增长,FirstBits的规模将非常差。 每个索引器或查找节点需要跟踪每个存在的比特币地址,并提供从别名到这些地址的快速查找。 随着网络线性增长,地址数量应该呈指数增长(假设(n-1)*(n-2)/ 2)的网络效应)使得该方案不可行。

部分merkle根类型的轻客户端依赖于可信第三方的别名查找。 考虑到他们在低资源设备上的典型使用情况,存储每个比特币地址的成本太高。 这个因素多于其他因素,意味着这个方案是次优的,必须被拒绝。

DNS TXT 记录

DNS允许创建包含任意数据的TXT记录。 在比特币别名系统中,由BIP标准共同定义的自定义格式将被用于存储从域名到比特币地址的映射。 这样的格式看起来不在本文的范围之内。

一个问题是,它要求那些希望创建这种映射的人熟悉配置DNS记录,并且能够运行必要的工具集来插入正确的数据。 虽然不是一个大问题,但这是一个可用性问题。

安全方面,DNS是不安全和不安全的设计。 可以通过与另一个主机位于同一网络来欺骗记录。 自二十世纪九十年代以来,DNSSEC幌子下的一系列修改工作已经开始,目前仍在进行中。

截至2011年12月,DNSSEC在互联网上还没有达到事实标准。 如果比特币网络的参与者希望使用DNS TXT记录,则他们除了必须配置DNS之外,还能够设置DNSSEC。 这可能是不可行的,尤其是在一些注册商只通过网页界面访问DNS的情况下。

DNS TXT记录的缺点是更新记录需要时间。 这鼓励人们不要使用每个具有一定安全问题的交易的新地址。

服务器服务

除了使用DNS TXT记录,另一种可能性是使用域名系统查找主机,然后联系预定义的端口上运行的服务,以获得比特币地址。

  1. 用户希望发送到foo@bar.net
  2. 客户端使用DNS查找bar.net的IP地址:123.123.123.123
  3. 客户端连接到端口123.123.123.123:4567,并请求用户foo的比特币地址
  4. 服务器响应地址或错误代码并终止连接。
  5. 客户将资金发送到地址

该服务将负责提供更改和存储服务映射的机制。 可以为希望使用该服务并在服务器上定制其帐户的用户提供前端Web界面。

这种方法具有积极的作用,为实现者提供最好的灵活性,然后将它们存储在数据库或纯文本文件中,然后使用通常用C编写的小型服务器端守护进程快速提供这些记录。这种方法具有高度可扩展性。

但是,这种方法也面临着依赖于DNS的问题,因此也容易受到欺骗。 因此DNSSEC也是必需的。 这种方法比DNS TXT记录稍好,因为它使得插入新用户和修改别名非常容易,这使得人们可以更便宜地运行这些服务器服务。

HTTPS Web服务

HTTPS通过加密连接提供额外的安全层,为用户提供非常需要的隐私。 与使用证书颁发机构一起,它解决了使用DNSSEC的问题,因为会引发错误,有人试图欺骗本地网络上的域名。

当试图发送到:

genjix@foo.org

这个请求在@的最后一个出现处被分解成句柄(genjix)和域(foo.org)。 客户端然后构造一个将查询地址的请求。

https://foo.org/bitcoin-alias/?handle=genjix

比特币别名已经被选为查询后缀,因为它允许这个系统在另一个web根目录中很容易共存,而不用担心名称冲突。

查询将返回一个用于付款的地址。

1Hd44nkJfNAcPJeZyrGC5sKJS1TzgmCTjjZ

每个查询是否返回一个唯一的地址,是否从一个预先存在的地址池中获取一个地址,等等的细节是每个服务器唯一的实现细节。 设置映射映射的别名依赖于可能具有Web界面的站点,并向用户提供免费服务,或者是为预先存在的地址提供私人定制服务。 这是留给管理层的政策,故意不在这里定义。

一个Web服务是微不足道的安装和成本低。 网络上有许多免费的提供商,允许任何具有最基本的网络技术知识的人创建自己的网站。 通过为用户提供一个软件包,任何人都可以快速设置自己的比特币别名。 它可以像PHP脚本一样简单,用户可以使用自定义设置进行编辑并上传自己的网站。

它也可以合理扩展 - 任何希望运行命名服务的人都可以使用各种数据库技术来附加后端,然后为用户提供一个Web前端来自定义和创建自己的别名。

作为一个例子,下面提供了一个天真的实现。

// resolv.h
#ifndef NOMRESOLV_H__
#define NOMRESOLV_H__

#include <string>
#include "curl/curl.h"

using std::string;

/*

这个类解决了服务器查找地址。
为了不与比特币地址冲突,我们在这里指的是人的手柄。
一个句柄的形式是:

   genjix@foo.org

大多数字符对用户名+密码有效(并进行相应处理),但域遵循通常的Web标准。 如果需要,可以粘贴一条路径,

   genjix@bar.com/path/to/

*/

class NameResolutionService
{
public:
    NameResolutionService();
    ~NameResolutionService();

    // Three main methods map to RPC actions.
    string FetchAddress(const string& strHandle, string& strAddy);

private:
    // A POST block
    class PostVariables
    {
    public:
        PostVariables();
        ~PostVariables();
        // Add a new key, value pair
        bool Add(const string& strKey, const string& strVal);
        curl_httppost* operator()() const;
    private:
        // CURL stores POST blocks as linked lists.
        curl_httppost *pBegin, *pEnd;
    };

    // Explodes user@domain => user, domain
    static void ExplodeHandle(const string& strHandle, string& strNickname, string& strDomain);
    // Perform the HTTP request. Returns true on success.
    bool Perform();

    // CURL error message
    char pErrorBuffer[CURL_ERROR_SIZE];
    // CURL response
    string strBuffer;
    // CURL handle
    CURL *curl;
};

#endif
// resolv.cpp
#include "resolv.h"

#include <boost/lexical_cast.hpp>

#include "access.h"

// callback used to write response from the server
static int writer(char *pData, size_t nSize, size_t nNmemb, std::string *pBuffer)
{
  int nResult = 0;
  if (pBuffer != NULL)
  {
    pBuffer->append(pData, nSize * nNmemb);
    // How much did we write?
    nResult = nSize * nNmemb;
  }
  return nResult;
}

NameResolutionService::NameResolutionService()
{
    // Initialise CURL with our various options.
    curl = curl_easy_init();
    // This goes first in case of any problems below. We get an error message.
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, pErrorBuffer);
    // fail when server sends >= 404
    curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
    curl_easy_setopt(curl, CURLOPT_HEADER, 0);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_302);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer);
    curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
    // server response goes in strBuffer
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &strBuffer);
    pErrorBuffer[0] = '\0';
}
NameResolutionService::~NameResolutionService()
{
    curl_easy_cleanup(curl);
}

void NameResolutionService::ExplodeHandle(const string& strHandle, string& strNickname, string& strDomain)
{
    // split address at @ furthrest to the right
    size_t nPosAtsym = strHandle.rfind('@');
    strNickname = strHandle.substr(0, nPosAtsym);
    strDomain = strHandle.substr(nPosAtsym + 1, strHandle.size());
}
bool NameResolutionService::Perform()
{
    // Called after everything has been setup. This actually does the request.
    CURLcode result = curl_easy_perform(curl);
    return (result == CURLE_OK);
}

string NameResolutionService::FetchAddress(const string& strHandle, string& strAddy)
{
    // GET is defined for 'getting' data, so we use GET for the low risk fetching of people's addresses
    if (!curl)
        // For some reason CURL didn't start...
        return pErrorBuffer;
    // Expand the handle
    string strNickname, strDomain;
    ExplodeHandle(strHandle, strNickname, strDomain);
    // url encode the nickname for get request
    const char* pszEncodedNick = curl_easy_escape(curl, strNickname.c_str(), strNickname.size());
    if (!pszEncodedNick)
        return "Unable to encode nickname.";
    // construct url for GET request
    string strRequestUrl = strDomain + "/bitcoin-alias/?handle=" + pszEncodedNick;
    // Pass URL to CURL
    curl_easy_setopt(curl, CURLOPT_URL, strRequestUrl.c_str());
    if (!Perform())
        return pErrorBuffer;
    // Server should respond with a JSON that has the address in.
    strAddy = strBuffer;
    return "";  // no error
}

NameResolutionService::PostVariables::PostVariables()
{
    // pBegin/pEnd *must* be null before calling curl_formadd
    pBegin = NULL;
    pEnd = NULL;
}
NameResolutionService::PostVariables::~PostVariables()
{
    curl_formfree(pBegin);
}
bool NameResolutionService::PostVariables::Add(const string& strKey, const string& strVal)
{
    // Copy strings to this block. Return true on success.
    return curl_formadd(&pBegin, &pEnd, CURLFORM_COPYNAME, strKey.c_str(), CURLFORM_COPYCONTENTS, strVal.c_str(), CURLFORM_END) == CURL_FORMADD_OK;
}

curl_httppost* NameResolutionService::PostVariables::operator()() const
{
    return pBegin;
}
</source>

<source lang="cpp">
// rpc.cpp
...

const Object CheckMaybeThrow(const string& strJsonIn)
{
    // Parse input JSON
    Value valRequest;
    if (!read_string(strJsonIn, valRequest) || valRequest.type() != obj_type)
        throw JSONRPCError(-32700, "Parse error");
    const Object& request = valRequest.get_obj();
    // Now check for a key called "error"
    const Value& error  = find_value(request, "error");
    // It's an error JSON! so propagate the error.
    if (error.type() != null_type)
        throw JSONRPCError(-4, error.get_str());
    // Return JSON object
    return request;
}

const string CollectAddress(const string& strIn)
{
    // If the handle does not have an @ in it, then it's a normal base58 bitcoin address
    if (strIn.find('@') == (size_t)-1)
        return strIn;

    // Open the lookup service
    NameResolutionService ns;
    // We established that the input string is not a BTC address, so we use it as a handle now.
    string strHandle = strIn, strAddy;
    string strError = ns.FetchAddress(strHandle, strAddy);
    if (!strError.empty())
        throw JSONRPCError(-4, strError);

    const Object& request(CheckMaybeThrow(strAddy));
    // Get the BTC address from the JSON
    const Value& address = find_value(request, "address");
    if (address.type() != str_type)
        throw JSONRPCError(-32600, "Server responded with malformed reply.");
    return address.get_str();
}

// Named this way to prevent possible conflicts.
Value rpc_send(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 2)
        throw runtime_error(
            "send <name@domain or address> <amount>\n"
            "<amount> is a real and is rounded to the nearest 0.01");

    // Intelligent function which looks up address given handle, or returns address
    string strAddy = CollectAddress(params[0].get_str());
    int64 nAmount = AmountFromValue(params[1]);
    // Do the send
    CWalletTx wtx;
    string strError = SendMoneyToBitcoinAddress(strAddy, nAmount, wtx);
    if (!strError.empty())
        throw JSONRPCError(-4, strError);
    return wtx.GetHash().GetHex();
}

...

IP交易

一个IP交易是比特币中一个旧的交易格式,被禁用,可能会被弃用。 它涉及被给予一个IP地址付款。 当连接到节点并使用“checkorder”请求他们的公钥时,他们将用以下格式的脚本进行响应:

<public key> OP_CHECKSIG

类似于coinbase输出交易。 IP事务具有能够包含额外的元数据的优点,这在许多事务中可能是有用的。 目前没有进行认证,使得该方案对中间人(MITM)攻击不安全。

此提案旨在为IP事务启用DNS查找。

“checkorder”消息将包含一个目标帐户,该目标帐户可映射到在同一主机下运行的不同的独立密钥对/钱包集。 从checkorder参考信息到本地系统的确切映射是实现定义的。

通过使用DNS查找,通过将公钥存储在DNS TXT记录中,可以缓解IP事务的MITM问题。 这个公钥将被用于所有将来从该主机发出的“回复”消息。 首次使用需要确认接受该公钥; 像SSH一样。 如果“回复”消息与接受的公钥不匹配,则主机将被给出错误。

Namecoin ID

此建议使用Namecoin区块链将别名与比特币地址相关联。 比特币查询名称币结点。 这将检索包含与该别名相关联的比特币地址的结构化数据。

使用像Namecoin这样的分散的域名系统,意味着不像这里列出的其他提案那样需要信任外部服务器或实体。 这表明系统具有高可用性和易于输入的优点(不限制用户创建别名)。

下面介绍两个例子。 第一个显示了一个更简单的格式,而第二个显示了几个比特币地址的结构化格式。

$ namecoind name_show id/khal
 {
   "bitcoin" : "1KHAL8bUjnkMRMg9yd2dNrYnJgZGH8Nj6T"
 }
 ```

$ namecoind name_show id/khal
{
“bitcoin” :
{
“default” : “1KHAL8bUjnkMRMg9yd2dNrYnJgZGH8Nj6T”,
“donation”: “1J3EKMfboca3SESWGrQKESsG1MA9yK6vN4”
}
}



更多可能性:

- 允许安全使用不安全的渠道

你可以把一个网址和一个比特币地址,用来签署的结果。 这意味着对这个URL的查询将返回一个比特币地址和签名。 比特币然后可以检查(使用verify_message函数)返回的地址还没有被另一个替换。

$ namecoind name_show id/khal
{
“bitcoin” :
{
“url” : “http://merchant.com/bitcoin/getnewaddres/“,
“signedWith” : “1KHAL8bUjnkMRMg9yd2dNrYnJgZGH8Nj6T”
}
}


- 允许每次或每个用户获得不同的地址,每个订单等

$ namecoind name_show id/khal
{
“bitcoin” :
{
“url” : “http://merchant.com/bitcoin/getaddres/{Your customer id}”,
“signedWith” : “1KHAL8bUjnkMRMg9yd2dNrYnJgZGH8Nj6T”,
“useOnce”: false
}
}
“`

在上面的例子中,比特币会要求用户输入“Your customer id”,然后在发出http请求之前将该值替换为url。 商家将收到请求,并为用户提供与该客户相关的付款地址。

任何文字可以被放入括号,允许商家以使其适应他们的需求。

  • 规范是可扩展的

可以稍后添加新功能来支持未发现的案例。
有关更多信息,请参阅Namecoin ID的规范。

总结

11年提出来的,现在被搁置,问题是比特币的地址太长,所以想通过别名的方式来解决。

原文档的连接已经失效了,但是内容还能谷歌出来。

参考资料

作者:diandianxiyu 发表于2017/11/9 9:35:09 原文链接
阅读:43 评论:0 查看评论

Spark Streaming中,增大任务并发度的方法有哪些?

$
0
0

Spark Streaming中,增大任务并发度的方法有哪些?


0 准备阶段

Q: 在Spark集群中,集群的节点个数、RDD分区个数、CPU内核个数三者与并行度的关系是什么?

我们先梳理一下Spark中关于并发度涉及的几个概念: File, Block, Split, Task, Partition, RDD以及节点数、Executor数、core数目的关系。




  1. 输入可能以多个文件的形式存储在HDFS上,每个File都包括了很多Block。
  2. 当Spark读取这些文件作为输入时,会根据具体数据格式对应的InputFormat进行解析,一般是将若干个Block合并成一个输入分片(InputSplit),注意InputSplit不能跨越文件。
  3. 随后将为这些输入分片生成具体的Task。InputSplit与Task是一一对应的关系。
  4. 这些具体的Task,每个都会被分配到集群上的某个节点的某个Executor去执行。
  • 每个节点可以起一个或多个Executor。
  • 每个Executor由若干core组成,每个Executor的每个core一次只能执行一个Task。
  • 每个Task执行的结果就是生成了目标RDD的一个partition。

Note:
这里的core是虚拟的core而不是机器的物理CPU核,可以理解为Executor的一个工作线程。

Task被执行的并发度 = Executor数目 * 每个Executor核数

至于partition的数目:
  • 对于数据读入阶段,例如: sc.textFile,输入文件被划分为多少InputSplit就会需要多少初始Task。
  • 在Map阶段,partition数目保持不变。
  • 在Reduce阶段,RDD的聚合会出发shuffle操作,聚合后的RDD的partition数目跟具体操作有关。例如:repartition操作会聚合成指定分区数,还有一些算子是可配置的。

1 Spark Streaming增大任务并发度
Q: 在Spark Streaming中,增大任务并发度的方法有哪些?
A:s1 增加Kafka的分区数
      s2 repartition
      s3 --num-executors
      s4 --executor-cores

1.1 解析

RDD在计算的时候,每个分区都会起一个task,所以RDD的分区数目决定了总的task数据。
申请的计算节点(Executor)数目和每个计算节点核数,决定了你同一时刻可以并行执行的task。
e g:
RDD有100个分区,那么计算的时候就会生成100个task,你的资源配置为10个计算节点,每个2个核,同一时刻可以并行的task数目为20,计算这个RDD就需要5个轮次。
如果计算资源不变,你有101个task的话,就需要6个轮次,在最后一轮中,只有一个task在执行,其余核都在空转。
如果资源不变,你的RDD只有两个分区,那么同一时刻只有2个task运行,其余18个核空转,造成资源浪费。
这就是在Spark调优中,通过增大RDD分区数目,进而增大任务并行度的做法。


Reference Link

[1] Spark Streaming和Kafka整合开发指南(一):https://www.iteblog.com/archives/1322.html

[2] Spark Streaming和Kafka整合开发指南(二):https://www.iteblog.com/archives/1326.html

[3] Spark Streaming性能调优详解: https://www.cnblogs.com/gaopeng527/p/4961701.html

作者:qq_17776287 发表于2017/11/9 9:44:17 原文链接
阅读:4 评论:0 查看评论

降维分析之PCA分析及实现

$
0
0

引言

不知道大家还记不记得前面我们分享 支持向量机(SVM)的分析及python实现时说过,当数据遇到线性不可分时,我们可以利用kernel技巧将低维数据映射到高维数据上,从而使得数据线性可分,这是个“升维”操作。那么本章我们就来分享个“降维”操作。

为什么要降维

众所周知,降维的目标就是对输入的数据进行削减,由此剔除数据中的噪声并提高机器学习方法的性能。那么为什么会有降维的操作呢?那是因为高维空间会出现样本稀疏、距离计算困难等问题,这些被我们称作“维数灾难”。缓解维数灾难的一个重要途径就是降维了。首先我们来分享第一个降维算法PCA。

PCA简单数学原理

主成分分析(Principal Component Analysis,简称PCA)是最常用的一种降维方法。我们有一个假设,即样本点处于一个正交属性空间。存在一个超平面能够将这些样本恰当的表达,同时该超平面还满足如下性质:

  • 最近重构型:样本点到这个超平面的距离都足够近
  • 最大可分性:样本点在超平面的投影能尽可能分开

基于这两个性质我们就能得到两种等价推导。这里我们不做推导的详细说明(详细过程,请戳:wiki),直接给条件,写出最后的结论。
假定数据样本进行了中心化,即ixi=0;再假定投影变换后得到的新坐标系为{w1,w2,...,wd},其中wi是标准正交基向量,即||wi||2=1,wTiwj=0,ij;最终通过一系列推导会得到:

XXTW=λW

于是我们只需对协方差矩阵XXT进行特征值分解,将求得的特征值排序:λ1λ2...λd,再取前d个特征值对应的特征向量构成W=(w1,w2,...,wd)。这就是主成分分析的解。

PCA代码实现

那么PCA的伪代码如下:

  • 去除平均值(中心化)
  • 计算协方差矩阵
  • 计算协方差矩阵的特征值和特征向量
  • 将特征值从大到小排序
  • 保留最上面的N个特征向量
  • 将数据转化到上述N个特征向量构建的新空间中
    具体代码如下:
def pca(dataMat, topNfeat=9999999):
    meanVals = mean(dataMat, axis=0)
    meanRemoved = dataMat - meanVals #remove mean
    covMat = cov(meanRemoved, rowvar=0)
    eigVals,eigVects = linalg.eig(mat(covMat))
    eigValInd = argsort(eigVals)            #sort, sort goes smallest to largest
    eigValInd = eigValInd[:-(topNfeat+1):-1]  #cut off unwanted dimensions
    redEigVects = eigVects[:,eigValInd]       #reorganize eig vects largest to smallest
    lowDDataMat = meanRemoved * redEigVects#transform data into new dimensions
    reconMat = (lowDDataMat * redEigVects.T) + meanVals
    return lowDDataMat, reconMat

我们采用一个有1000个数据点组成的数据集对其进行PCA降维,运行效果:

import importlib
import pca
importlib.reload(pca)
dataMat = pca.loadDataSet('testSet.txt')
lowDMat,reconMat = pca.pca(dataMat,1)
import numpy as np 
np.shape(lowDMat)
(1000, 1)
import matplotlib
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(dataMat[:,0].flatten().A[0],dataMat[:,1].flatten().A[0],marker='^',s=90)
<matplotlib.collections.PathCollection at 0x1d8b72b6ac8>
ax.scatter(reconMat[:,0].flatten().A[0],reconMat[:,1].flatten().A[0],marker='o',s=50,c='red')
<matplotlib.collections.PathCollection at 0x1d8b6edb438>
plt.show()

oca

总结

降维技术使得数据变得更易使用,并且它们往往能去除数据中的噪声,使得其他机器学习任务更加精确。

作者:u010665216 发表于2017/11/9 10:17:43 原文链接
阅读:0 评论:0 查看评论

H5之 Canvas图形实现

$
0
0
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <!--定义一个画布    -->
        <canvas id="mycanvas" width="100" height="100">测试浏览器</canvas>
        <!--这种写法,js要位于html的下方-->
        <script language="JavaScript">
            //根据id,来得到网页上的画布元素对象
            var c=document.getElementById("mycanvas");
            var ctx=c.getContext("2d");//2d内容
            //1.预备;
            ctx.beginPath();
            //2.设置起点;,就是设置起点的(x,y)坐标
            ctx.moveTo(10,10);
            //3.移动到终点;
            ctx.lineTo(100,10);
            ctx.lineTo(10,100);
            ctx.lineTo(100,100);
            
            ctx.lineTo(10,10);
            //4.绘制轮廓;
            ctx.stroke();
        </script>
    </body>

</html>

图形效果:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script language="JavaScript">
            //定义一个函数,类似于java的方法
            function drawRect(){
                var c=document.getElementById("mycanvas2");
                var ctx=c.getContext("2d");
                //压缩代码
                ctx.fillStyle="yellow";//填充样式
                ctx.lineWidth=10;
                ctx.strokeStyle="blueviolet";
                ctx.strokeRect(20,20,50,100);
                ctx.fillRect(20,20,50,100);
                ctx.clearRect(30,30,30,20);
//                ctx.beginPath();
//                ctx.rect(10,10,100,50);//绘制矩形
//                ctx.stroke();
            }
            //把自定义的函数,加到load事件监听中
            //addEventLister在整个页面加载完毕去添加响应;
            window.addEventListener("load",drawRect,true);//js可以位于上方;
            //window.onload=drawRect(); 这个还是js在下面
        </script>
        <canvas id="mycanvas2" width="500" height="500"></canvas>
    </body>
</html>

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script>
            function drawArc(){
                var c=document.getElementById("mycanvas3");
                var ctx=c.getContext("2d");
                ctx.beginPath();
                //起始度:弧度;这个地方不是度
                //PI 弧度=180度
                ctx.strokeStyle="red";//设置轮廓的样式
                ctx.arc(50,50,40,Math.PI/2,2*Math.PI,false);//绘制
                ctx.stroke();
            }
            window.addEventListener("load",drawArc,true);
        </script>
        <canvas id="mycanvas3" width="500" height="500"></canvas>
    </body>
</html>





作者:zhangchen124 发表于2017/11/9 10:26:33 原文链接
阅读:0 评论:0 查看评论

CCF CSP 2016年04月第4题 游戏 (BFS)

$
0
0

问题描述
试题编号: 201604-4
试题名称: 游戏
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
  小明在玩一个电脑游戏,游戏在一个n×m的方格图上进行,小明控制的角色开始的时候站在第一行第一列,目标是前往第n行第m列。
  方格图上有一些方格是始终安全的,有一些在一段时间是危险的,如果小明控制的角色到达一个方格的时候方格是危险的,则小明输掉了游戏,如果小明的角色到达了第n行第m列,则小明过关。第一行第一列和第n行第m列永远都是安全的。
  每个单位时间,小明的角色必须向上下左右四个方向相邻的方格中的一个移动一格。
  经过很多次尝试,小明掌握了方格图的安全和危险的规律:每一个方格出现危险的时间一定是连续的。并且,小明还掌握了每个方格在哪段时间是危险的。
  现在,小明想知道,自己最快经过几个时间单位可以达到第n行第m列过关。
输入格式
  输入的第一行包含三个整数nmt,用一个空格分隔,表示方格图的行数n、列数m,以及方格图中有危险的方格数量。
  接下来t行,每行4个整数rcab,表示第r行第c列的方格在第a个时刻到第b个时刻之间是危险的,包括ab。游戏开始时的时刻为0。输入数据保证rc不同时为1,而且当rnc不为m。一个方格只有一段时间是危险的(或者说不会出现两行拥有相同的rc)。
输出格式
  输出一个整数,表示小明最快经过几个时间单位可以过关。输入数据保证小明一定可以过关。
样例输入
3 3 3
2 1 1 1
1 3 2 10
2 2 2 10
样例输出
6
样例说明
  第2行第1列时刻1是危险的,因此第一步必须走到第1行第2列。
  第二步可以走到第1行第1列,第三步走到第2行第1列,后面经过第3行第1列、第3行第2列到达第3行第3列。
评测用例规模与约定
  前30%的评测用例满足:0 < nm ≤ 10,0 ≤ t < 99。
  所有评测用例满足:0 < nm ≤ 100,0 ≤ t < 9999,1 ≤ r ≤ n,1 ≤ c ≤ m,0 ≤ a ≤ b ≤ 100。
解题思路:数组maze用来表示需要遍历的图,并且记录是在哪个时间点被访问;数组start用来记录危险区域的开始时间;数组end用来记录危险记录的结束时间;数组step用来记录行走的步数,变量time用来记录当前时间点。考虑好条件后直接BFS就可以了。

代码如下:

#include <iostream>
#include <queue>

using namespace std;
const int maxn = 105;
const int maxt = 10000;
typedef pair<int, int> P;

int dir[4][2] = {{0, 1}, {-1, 0}, {0, -1}, {1, 0}};
int maze[maxn][maxn], start[maxn][maxn], end[maxn][maxn], step[maxn][maxn];
int n, m, t;

int bfs() {
    queue<P> que;
    que.push(P(1, 1));
    int time = 1;
    step[1][1] = 0;
    while(que.size()) {
        P p = que.front();
        que.pop();
        if(p.first == n && p.second == m) {
            return step[p.first][p.second];
        }
        for(int i = 0; i < 4; i++) {
            int nx = p.first + dir[i][0];
            int ny = p.second + dir[i][1];
            if(nx < 1 || nx > n || ny < 1 || ny > m || maze[nx][ny] == time)
                continue;
            time = step[p.first][p.second] + 1;
            if((start[nx][ny] == 0 && end[nx][ny] == 0) || !(start[nx][ny] <= time && time <= end[nx][ny])) {
                que.push(P(nx, ny));
                step[nx][ny] = step[p.first][p.second] + 1;
				maze[nx][ny] = time;//保存上一次被访问的时间
            }
        }
    }
    return -1;
}

int main(void) {
    cin >> n >> m >> t;
    int r, c, a, b;
    for(int i = 0; i < t; i++) {
        cin >> r >> c >> a >> b;
        start[r][c] = a;
        end[r][c] = b;
    }
    cout << bfs() << endl;
    return 0;
}


作者:qq_26658823 发表于2017/11/8 22:09:24 原文链接
阅读:23 评论:0 查看评论

LeetCode Median of Two Sorted Arrays

$
0
0

一、题目

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

Example 1:
nums1 = [1, 3]
nums2 = [2]

The median is 2.0
Example 2:
nums1 = [1, 2]
nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5

二、代码

简单合并,排序,取中值

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { 
        int *it;
        int m = nums1.size()?nums1.size():0;
        int n = nums2.size()?nums2.size():0;
        int *merge = new int[m+n];

        if(!nums1.empty()){
            it = &*nums1.begin();
            memcpy(merge,it,sizeof(int)*m);   
        }
        if(!nums2.empty()){
            it = &*nums2.begin();
            memcpy(merge+m,it,sizeof(int)*n);  
        }
        sort(merge,merge+m+n);
        //注意数组下标和计算的下标区别
        return (m+n)%2?1.0*merge[(m+n)>>1]:(merge[(m+n-1)>>1]+merge[(m+n)>>1])/2.0;

    }
};
作者:qq_16234613 发表于2017/11/8 22:09:36 原文链接
阅读:40 评论:0 查看评论

【GitChat】每日精选20171109——双 11 大前端工程师读书清单

$
0
0

GitChat 是一款基于微信平台的 IT 阅读/写作互动产品。我们的目的是通过这款产品改变 IT 知识的学习方式,让专业读者获得自主选择权,让知识分享者获得收益。

关于GitChat 你想知道的都在这里

在这里每天会为大家带来最新的Chat分享,挑一个你感兴趣的话题,来一场Chat,赴一场约会吧!


双 11 大前端工程师读书清单

这里写图片描述

在电商巨头们近期敲锣打鼓的宣传下,我们的购书车单也越来越长。“穷。”,是我们深深的感叹。那么该如何从众多的书籍中优选出适合自己的呢?除了向自己周围年长的人请教该读什么书外,我们还能有什么渠道促进大家更多的交流呢?

点我去订阅本场Chat


前端工程师“应试”指南

这里写图片描述

技术面试的关键点到底是什么?面试官和 HR 关注的重点到底是什么?如何“突击”一场完美的面试?什么简历可以脱颖而出引起注意?

点我去订阅本场Chat


如何成为一枚高贵的 PHP 攻城狮

这里写图片描述

随着 PHP 的进步与其它技术的发展,涌现了一些新的开发理念与模式。本篇文章不是讲解 PHP 基础知识,而是讲解一些对编码,对开发,对生产闭环更有益的知识,让你成为有思想的高贵的攻城狮,而非复制粘贴的搬运工亦或翻译机。

点我去订阅本场Chat


Vue.2x 源码分析之响应式原理

这里写图片描述

这场 Chat 之后你就会知道并理解在 Vue.js 的数据驱动中如何将数据和试图连接起来并实现响应式。内容主要包括:

如何对数据进行监听,也就是基于依赖收集的观测机制(Observer);
每一个指令都会有一个对应的用来观测数据的对象,叫做 watcher;
如何解析 template 中的指令,形成 dom 树,最终编译成真实的 dom。
从源码角度看 Vue.js 的生命周期是如何实现的。
Vue.js 的异步批量更新,动画系统等。

点我去订阅本场Chat


如何用静心提升创造力?

这里写图片描述

我一直认为自己是个没有什么创造力的人,只是执行力还不错。直到 13 年,我接触到来自印度的“静心”(meditation),这几年来每天一个小时的练习,并且持续的跟着几位印度老师的学习,我的创造力就像泉水般源源不断的涌出。

点我去订阅本场Chat


Hackintosh:每个人都能 DIY 的苹果电脑

这里写图片描述

也许你对 Macbook 神往已久,但是苦于兜里没有足够的钞票;也许你是爱折腾 geek,想要试试 PC 与 OS X 合体的感觉;或者你又想追求极致的性能,看不上苹果电脑的配置?

本场 Chat 我将为你从头到尾介绍一遍黑苹果的配置安装及使用技巧,希望你也能够在参加活动之后 DIY 出一台属于自己的黑苹果笔记本电脑。

点我去订阅本场Chat


希望大家在交流过程中,结识更多多志同道合的朋友。一起来加入 GitChat 作者俱乐部,结识行业大牛吧!

这里写图片描述

作者:blogdevteam 发表于2017/11/9 10:49:25 原文链接
阅读:172 评论:0 查看评论

LeetCode Longest Palindromic Substring

$
0
0

一、试题

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example:

Input: “babad”

Output: “bab”

Note: “aba” is also a valid answer.
Example:

Input: “cbbd”

Output: “bb”

二、代码

传统方法,逐渐遍历查找。

class Solution {
public:
    string longestPalindrome(string s) {
    pair<int,int> pos;
    int max=0;
    for(int i=0; i<s.size(); i++){
        int st=i,en=i;
        while(s[st]==s[en] && st>=0 && en<s.size()){
            st--,en++;
        }
        if(en-st-1>max){
            max = en-st-1;
            pos = make_pair(st+1,en);
        }
        if(i+1<s.size() && s[i]==s[i+1]){
            int st=i,en=i+1;
            while(s[st]==s[en] && st>=0 && en<s.size()){
                st--,en++;
            }
            if(en-st-1>max){
                max = en-st-1;
                pos = make_pair(st+1,en);
            }           
        }
    }
    cout<<pos.first<<pos.second;

    return s.substr(pos.first,pos.second-pos.first);  
    }
};
作者:qq_16234613 发表于2017/11/9 11:32:17 原文链接
阅读:71 评论:0 查看评论

特征分析之SVD

$
0
0

引言

前面我们分享降维分析之PCA分析及实现,说PCA除了应用在数据降维上,还可用于特征分析。今天我们就来分享个新的特征分析的方法,叫做奇异值分解(Singular Value Decomposition,SVD)。

SVD背后的数学原理

我们如果在Google搜索引擎中输入SVD这个单词,会弹出好多图片,如下其中一幅:
svd;如果我们在Baidu搜索引擎中搜索SVD的话,百度百科的解释是这样的:SVD德拉贡诺夫狙击步枪的英文缩写。哈哈哈~,咱们这次可不是给大家来个军事武器普及,言归正传,我们来看看SVD背后的数学原理。

SVD的起源

奇异值分解技术(简称SVD)的历史很长,也有些令人惊讶。它开始于社会科学和智力测试。早期的情报研究人员指出,用来测量智力不同方面的测试,比如语言和空间,往往是紧密相关的。

矩阵分解

矩阵分解可以将原矩阵表示成新的易于处理的形式,这个新形式是两个或多个矩阵的乘积。最常见的一种矩阵分解技术就是SVD。
奇异值分解是一种将一个矩阵分解成三个矩阵的方法:

Datam×n=Um×mΣm×nVTn×n

上述分解会构建出一个矩阵Σ,该矩阵只有对角元素,其他元素均为0。Σ的对角元素是从大到小排列的。这些对角元素称为奇异值,它们对应原始数据集中的重要特征。

python实现

SVD在Numpy中有现成的工具箱linalg。使用起来很简单:

U,Sigma,V = linalg.svd(Data)

总结

SVD的数学原理很简单,并且实现也不复杂,但是SVD在很多领域有着极其广泛的应用。最典型的就是推荐系统,博主准备找个合适的时间来做个应用SVD的demo,然后给大家分享。

作者:u010665216 发表于2017/11/9 11:56:36 原文链接
阅读:33 评论:0 查看评论

【蓝桥杯】【趣味算式】

$
0
0

题目
匪警请拨110,即使手机欠费也可拨通!
为了保障社会秩序,保护人民群众生命财产安全,警察叔叔需要与罪犯斗智斗勇,因而需要经常性地进行体力训练和智力训练!
某批警察叔叔正在进行智力训练:
1 2 3 4 5 6 7 8 9 = 110;
请看上边的算式,为了使等式成立,需要在数字间填入加号或者减号(可以不填,但不能填入其它符号)。
之间没有填入符号的数字组合成一个数,例如:12+34+56+7-8+9 就是一种合格的填法;123+4+5+67-89 是另一个可能的答案。
请你利用计算机的优势,帮助警察叔叔快速找到所有答案。
每个答案占一行。形如:

12+34+56+7-8+9
123+4+5+67-89
......

已知的两个答案可以输出,但不计分。
各个答案的前后顺序不重要。
注意:
请仔细调试!您的程序只有能运行出正确结果的时候才有机会得分!
请把所有类写在同一个文件中,调试好后,存入与【考生文件夹】下对应题号的“解答.txt”中即可。
相关的工程文件不要拷入。
请不要使用package语句。
源程序中只能出现JDK1.5中允许的语法或调用。不能使用1.6或更高版本。

分析
题目可以看成在9个数字中间填8个符号,引入#符号,作为分隔符,表示两边的数字需要连起来。
8个位置每个位置都可能填入三种符号:#、+、-。
递归遍历所有的可能性,然后计算结果,满足条件的算式就加入到集合中,最后打印出来。

源码

    private static HashSet<String> sets = new HashSet<String>();

    public static void main(String[] args) {

        //创建一个17个元素的字符数组,留8个空档,即偶数下标填充数字,奇数下标留着
        char[] a = new char[17];
        char x = '1';
        for (int i = 0; i < a.length; i+=2) {
            a[i] = x++;
        }

        f1(a, 1);

        for (String c : sets) {
            System.out.println(c);
        }

    }

    private static void f1(char[] a, int index) {
        //递归出口
        if(index == 17) {
            f2(a);
            return;
        }

        //该位置可能是#,表示没有运算符,应该连接两端数字
        a[index] = '#';
        f1(a, index+2);
        //该位置可能
        a[index] = '+';
        f1(a, index+2);

        a[index] = '-';
        f1(a, index+2);
    }

    private static void f2(char[] a) {
        //装数字
        ArrayList<String> list1 = new ArrayList<String>();
        //装运算符
        ArrayList<Character> list2 = new ArrayList<Character>();

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < a.length; i++) {
            if(a[i] == '+' || a[i] == '-') {
                list2.add(a[i]);
                list1.add(sb.toString());
                sb = new StringBuilder();
            }else if(a[i] == '#') {
                continue;
            }else {
                sb.append(a[i]);
            }
        }
        list1.add(sb.toString()); //不要忘记把最后一个数字加上

        if(list1.size() == 0 || list2.size() == 0) {
            return;
        }

        //计算等式的值
        int sum = Integer.valueOf(list1.get(0));
        for (int i = 0; i < list2.size(); i++) {
            if(list2.get(i) == '+') {
                sum += Integer.valueOf(list1.get(i+1));
            }

            if(list2.get(i) == '-') {
                sum -= Integer.valueOf(list1.get(i+1));
            }
        }

        //满足条件就将等式字符串加入到集合中
        if(sum == 110) {
            String ret = String.valueOf(a);
            String[] ss = ret.split("#");
            StringBuilder sb2=  new StringBuilder();
            for (int i = 0; i < ss.length; i++) {
                sb2.append(ss[i]);
            }
            sets.add(sb2.toString());
        }
    }

结果
12+34+56+7-8+9
1+234-56-78+9
123-4-5+6+7-8-9
1+2+34+5+67-8+9
123+4+5+67-89
123+4-5-6-7-8+9
123-4+5-6-7+8-9
12+3+45+67-8-9
1-2+3+45-6+78-9
12-3+4-5+6+7+89

一共找到10种满足条件的等式

作者:bear_huangzhen 发表于2017/11/9 11:57:43 原文链接
阅读:37 评论:0 查看评论

洛谷P2023 [AHOI2009]维护序列 (BZOJ1798)

$
0
0

线段树

洛谷题目传送门
BZOJ题目传送门

裸的双Tag线段树。。。(不过我都没打过双Tag的线段树)
BZOJMLE说我TLE。。。害我郁闷了半天。。。
注意BZOJ只给64MB
还有就是一定要先乘后加
具体见注释

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 150000
using namespace std;
typedef long long LL;//开LL保险一点
struct tree{
    LL l,r;
    LL sum;
}t[MAXN*4+5];
LL n,m,MOD;
LL a[MAXN+5],lazy1[MAXN*4+5],lazy2[MAXN*4+5];//1表示加,2表示乘
inline char readc(){//fread读优
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
inline LL _read(){
    LL num=0; char ch=readc();
    while (!isdigit(ch)) ch=readc();
    while (isdigit(ch)) { num=num*10+ch-48; ch=readc(); }
    return num;
}
void build(LL l,LL r,LL num){//建树
    t[num].l=l,t[num].r=r;
    if (l==r){//给叶子结点赋值
        t[num].sum=a[l];
        return;
    }
    build(l,(l+r)/2,num*2);
    build((l+r)/2+1,r,num*2+1);
    t[num].sum=(t[num*2].sum+t[num*2+1].sum)%MOD;
}
void pushdown(LL num){//传Tag
    if (lazy2[num]!=1){//先算乘法
        (lazy2[num*2]*=lazy2[num])%=MOD;
        (lazy2[num*2+1]*=lazy2[num])%=MOD;
        t[num*2].sum=(t[num*2].sum*lazy2[num])%MOD;
        t[num*2+1].sum=(t[num*2+1].sum*lazy2[num])%MOD;
        (lazy1[num*2]*=lazy2[num])%=MOD;
        (lazy1[num*2+1]*=lazy2[num])%=MOD;
        lazy2[num]=1;//1
    }
    if (lazy1[num]){//再算加法
        (lazy1[num*2]+=lazy1[num])%=MOD;
        (lazy1[num*2+1]+=lazy1[num])%=MOD;
        (t[num*2].sum+=((t[num*2].r-t[num*2].l+1)*lazy1[num])%MOD)%=MOD;
        (t[num*2+1].sum+=((t[num*2+1].r-t[num*2+1].l+1)*lazy1[num])%MOD)%=MOD;
        lazy1[num]=0;//0
    }
}
void nsrt(LL l,LL r,LL w,bool flag,LL num){//插入(修改)
    if (t[num].l>=l&&t[num].r<=r){
        if (!flag){//如果是乘操作
            t[num].sum=(t[num].sum*w)%MOD;
            lazy2[num]=(lazy2[num]*w)%MOD;
            lazy1[num]=(lazy1[num]*w)%MOD;//别忘了给加Tag乘
        }
        else{
            t[num].sum=(t[num].sum+((t[num].r-t[num].l+1)*w)%MOD)%MOD;
            lazy1[num]=(lazy1[num]+w)%MOD;
        }
        return;
    }
    if (t[num].l>r||t[num].r<l) return;
    if (lazy1[num]||lazy2[num]!=1)
        pushdown(num);
    nsrt(l,r,w,flag,num*2);
    nsrt(l,r,w,flag,num*2+1);
    t[num].sum=(t[num*2].sum+t[num*2+1].sum)%MOD;
}
LL srch(LL l,LL r,LL num){
    if (t[num].l>=l&&t[num].r<=r) 
        return t[num].sum;
    if (t[num].r<l||t[num].l>r) 
        return 0;
    if (lazy1[num]||lazy2[num]!=1)
        pushdown(num);
    LL x=srch(l,r,num*2);
    LL y=srch(l,r,num*2+1);
    return (x+y)%MOD;
}
int main(){
    n=_read(),MOD=_read();
    for (LL i=1;i<=n;i++)
        a[i]=(_read())%MOD;
    m=_read();
    for (LL i=1;i<=MAXN*4;i++)
        lazy2[i]=1;
    build(1,n,1);
    while (m--){
        LL flag=_read();
        switch (flag){
            case 1:{
                LL l=_read(),r=_read(),w=_read();
                nsrt(l,r,w,0,1);
                break;
            }
            case 2:{
                LL l=_read(),r=_read(),w=_read();
                nsrt(l,r,w,1,1);
                break;
            }
            default:{
                LL l=_read(),r=_read();
                printf("%d\n",srch(l,r,1));
                break;
            }
        }
    }
    return 0;
}
作者:a1799342217 发表于2017/11/9 13:51:20 原文链接
阅读:13 评论:0 查看评论

LeetCode-718:Maximum Length of Repeated Subarray (最长公共子数组) -- medium

$
0
0

Question

Given two integer arrays A and B, return the maximum length of an subarray that appears in both arrays.

Example 1:

Input:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
Output: 3

Explanation: 
The repeated subarray with maximum length is [3, 2, 1].

Note:

  • 1 <= len(A), len(B) <= 1000;
  • 0 <= A[i], B[i] < 100。

问题解析:

给定两个数组A和B,寻找A和B中最长的公共子数组的长度。(注意这里可以不是连续的元素)

Answer

Solution 1:

DP,动态规划。
(关于动态规划相关知识后续后写一篇总结,敬请关注)

  • 利用动态规划的思想,首先寻找问题的最优子结构,给出问题的状态转移方程;
  • 利用 dp[i][j] 来存储A[i]和B[j]之前的LCS(最长公共子序列);
  • A[n]=B[m]时,dp[i][j] = dp[i-1][j-1] + 1;max = Math.max(max, dp[i][j])
class Solution {
    public int findLength(int[] A, int[] B) {
        if (A == null || B == null) return 0;
        int m = A.length;
        int n = B.length;
        int max = 0;
        //the length of longest common subarray end with A[i] and B[j]
        int[][] dp = new int[m+1][n+1];
        for (int i =0; i <= m; i++){
            for (int j = 0 ; j <= n; j++){
                if (i == 0 || j == 0){
                    dp[i][j] = 0;
                }
                else{
                    if (A[i-1] == B[j-1]){
                        dp[i][j] = dp[i-1][j-1] + 1;
                        max = Math.max(max, dp[i][j]);
                    }
                }
            }
        }

        return max;
    }
}
  • 时间复杂度:O(n^2),空间复杂度:O(n^2)

Solution 2:

同样还是DP算法,但其计算复杂度更低。

  • 下面的这种解法仅用了1D的dp数组。这是Discuss区的部分快速解法的答案;
  • 通过我后面详细的分析,最后发现,这种解法是针对“最长连续公共子序列”的问题所设计的,一旦涉及不连续的元素,dp[]中的值全部都会置为0;而我们上面的一种解法是既可以同时解决不连续子序列的问题的。
  • 既然这种解法可以通过,那么说明这个题目的初始设置想法就是求“最长连续公共子序列”。
class Solution {
    public int findLength(int[] A, int[] B) {
        int ans = 0;
        int[] dp = new int[A.length + 1];
        for (int j = 1; j <= B.length; j++) {
            for (int i = A.length; i >= 1; i--) {
                if (A[i - 1] == B[j - 1]) {
                    dp[i] = dp[i - 1] + 1;
                } else {
                    dp[i] = 0;
                }
                ans = Math.max(ans, dp[i]);
            }
        }
        return ans;
    }
}
  • 时间复杂度:O(n^2),空间复杂度:O(n)

关于动态规划问题,可以先参考我在《算法导论》中的总结文章:动态规划问题

作者:Koala_Tree 发表于2017/11/9 13:40:27 原文链接
阅读:31 评论:0 查看评论

linux\mac 日常入门命令行使用——搜索文件\文件夹

$
0
0

linux\mac 日常入门命令行使用——搜索文件\文件夹

搜索文件或者文件夹,是一个常见的需求。我们可以用多种命令来实现我们的需求。

find 命令实现搜索

find 是英文,寻找的意思。这个命令可以很方面的来搜索我们需要的内容。

标准命令如下:

find ./ -iname "*.txt"

命令 搜索的目录 参数 关键词

find

-iname 是不区分大小写。如果要区分大小写的话 -name 即可。一般情况下,我们搜索内容都是不区分大小写的。

关键词可以用 * 号进行通配。事实上,也支持正则表达式。不过我估计你可能不熟悉正则表达式,所以不强求了。

这是标准用法。但是我下面再推荐一个我喜欢的组合命令的用法

find + grep 搜索

默认的 find 命令,功能及其强大,并且最基础的也需要知道一个 * 这样的通配符。但是通过这个组合命令,可以让你啥都不懂的进行任意的搜索。

我们知道 find ./ 命令,可以把当前文件夹下的所有内容全部列出来。同时 grep 命令可以根据关键词进行过滤。然后我们就可以组合这个命令了。

find ./ | grep txt

这个命令就可以将当前目录里面的所有文件名中包含 txt 的全部列出来。

find+grep

另外,我们还可以组合多个关键词进行进一步的过滤,只要在后面接着输入 | grep 关键词即可。

最重要的是,还可以取反,就是 grep 加上 -v 这个参数。

举例如下:

find ./ | grep txt | grep Site
find ./ | grep txt | grep Site | grep -v linux

find+grep2

看,重要我们就可以不用管什么正则,什么通配符,用我们简单的组合命令,就可以进行我们想要的任意搜索了。

| 是管道的意思。作用是把前面的命令的结果传给后面的命令继续去执行。这是命令行中非常重要并且非常好用的概念。我们可以用这些来进行很多的组合操作。

对了,写完了才想起来,我们用不着用 find ./ 作为第一个命令,还可以用 find . 作为命令。效果是一样的。嘿嘿。

本文由 FungLeo 原创,允许转载,但转载必须保留首发链接。

作者:FungLeo 发表于2017/11/9 14:43:07 原文链接
阅读:29 评论:0 查看评论

机器学习之条件随机场(CRF)

$
0
0

什么是CRF

CRF即条件随机场(Conditional Random Fields),是在给定一组输入随机变量条件下另外一组输出随机变量的条件概率分布模型,它是一种判别式的概率无向图模型,既然是判别式,那就是对条件概率分布建模。

CRF较多用在自然语言处理和图像处理领域,在NLP中,它是用于标注和划分序列数据的概率化模型,根据CRF的定义,相对序列就是给定观测序列X和输出序列Y,然后通过定义条件概率P(Y|X)来描述模型。

CRF的输出随机变量假设是一个无向图模型或者马尔科夫随机场,而输入随机变量作为条件不假设为马尔科夫随机场,CRF的图模型结构理论上可以任意给定,但我们常见的是定义在线性链上的特殊的条件随机场,称为线性链条件随机场。

概率无向图模型

前面说到CRF的输出随机变量是一个概率无向图模型,那么现在看看该模型。

概率无向图模型是由无向图表示的联合概率分布,假设联合概率分布P(Y)通过无向图来表示,则在图中节点表示随机变量,边表示随机变量之间的依赖关系,联合概率分布P(Y)满足马尔科夫性则称其为概率无向图模型,或者是马尔科夫随机场。

如下图,图是一个由节点和边组成的结构体,无向是指边没有方向,整个图记作G=(V,E),其中V为节点的集合,E为边的集合。

这里写图片描述

每个节点v对应一个随机变量Yv,于是Y=Yv|vV,在观察序列X的条件下,每个随机变量Yv都满足马尔科夫特性,即

P(Yv|X,Yw)=p(Yv|X,Yw,wv) ,其中wv表示w和v是图G中邻近的两个节点。

线性链条件随机场

无向图的结构理论上可以是任意的,但在NLP中对于标记处理问题,对其建模主要用最简单最普通的链式结构,即线性链条件随机场。如下图,可以看到节点为线性链结构,节点对应了序列Y的元素,而观察序列X不做任何独立性假设,但X序列的结构也可以是线性链结构。

这里写图片描述

综上所述,设有线性链结构的随机变量序列 X=(X1,X2,...,Xn),Y=(Y1,Y2,...,YN),在给定观察序列X的条件下,随机变量序列Y的条件概率分布为P(Y|X),若其满足马尔科夫特性,即
P(Yi|X,Y1,Y2...Yn)=P(Yi|X,Yi1,Yi+1),这时P(Y|X)则为线性链条件随机场。

概率的定义

在线性链条件随机场中,在给定的观察序列X情况下,某个特定序列Y的概率为P(Y|X),根据定义有,

P(Y|X)=exp(i,kλktk(Yi1,Yi,X,i)+i,lμlsl(Yi,X,i))

其中,tk(Yi1,Yi,X,i)表示转移函数,表示在序列X下序列Y在位置i-1及i对应的值转移概率,而sl(Yi,X,i)表示状态函数,表示在序列X下序列Y在位置i对应的值概率。另外λk,μl分别为两个函数的权重。

转移函数和状态函数都称为特征函数,特征函数一般取值0或1,满足特征函数的则为1,否则为0。比如下面的转移函数,只有当Yi1,Yi满足一定的条件时才为1,否则为0。

tk(Yi1,Yi,X,i)={1,0,conditionsaboutYi1,Yiotherwise

如果我们令sl(Yi,X,i)=sl(Yi1,Yi,X,i),则转移函数和状态函数可以统一由特征函数表示,对特征在各个位置i求和,有

Fk(Y,X)=ni=1fk(Yi1,Yi,X,i)

最后再加上归一化,最终条件随机场的条件概率为,

P(Y|X)=1Z(X)exp(Kk=1λkFk(Y,X))

其中,
Z(X)=yexp(Kk=1λkFk(Y,X))

如何训练CRF

训练CRF主要就是要训练特征函数的权重,对于训练集(x1,y1),(x2,y2),...,(xn,yn),采用极大似然估计法计算权重参数,条件概率的对数似然函数为:

L(λ)=x,yp~(x,y)ni=1(Kk=1λkfk(yi1,yi,x,i))xp~(x)logZ(x)

其中p~(x,y)为训练样本集中xy的经验概率,它等于xy同时出现的次数除以样本空间容量;p~(x)为训练样本集中x的经验概率,它等于x出现的次数除以样本空间容量。

然后对λ求导,令其为0再求解出λ,即得到解。因为极大似然估计法不一定能得到一个近似解,所以需要利用一些迭代技术来确定参数,比如GIS或IIS算法,这里不再深入。

啥时考虑CRF

如果信息是与时间或空间的前后有关联时要考虑到CRF。

以下是广告

========广告时间========

鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以到 https://item.jd.com/12185360.html 进行预定。感谢各位朋友。

为什么写《Tomcat内核设计剖析》

=========================

欢迎关注:

这里写图片描述
这里写图片描述

作者:wangyangzhizhou 发表于2017/11/9 14:46:13 原文链接
阅读:29 评论:0 查看评论

洛谷3938 斐波那契

$
0
0

标签:数论,LCA,树

http://www.yjjr.org/nd.jsp?id=14#_np=2_327

题目背景

大样例下发链接:http://pan.baidu.com/s/1c0LbQ2 密码:jigg

题目描述

小 C 养了一些很可爱的兔子。 有一天,小 C 突然发现兔子们都是严格按照伟大的数学家斐波那契提出的模型来进行 繁衍:一对兔子从出生后第二个月起,每个月刚开始的时候都会产下一对小兔子。我们假定,在整个过程中兔子不会出现任何意外。

小 C 把兔子按出生顺序,把兔子们从1 开始标号,并且小 C 的兔子都是 1 号兔子和 1 号兔子的后代。如果某两对兔子是同时出生的,那么小 C 会将父母标号更小的一对优先标号。

如果我们把这种关系用图画下来,前六个月大概就是这样的:


其中,一个箭头 A → B 表示 A 是 B 的祖先,相同的颜色表示同一个月出生的兔子。

为了更细致地了解兔子们是如何繁衍的,小 C 找来了一些兔子,并且向你提出了 m 个 问题:她想知道关于每两对兔子 aia_iai​ 和 bib_ibi​ ,他们的最近公共祖先是谁。你能帮帮小 C 吗?

一对兔子的祖先是这对兔子以及他们父母(如果有的话)的祖先,而最近公共祖先是指 两对兔子所共有的祖先中,离他们的距离之和最近的一对兔子。比如,5 和 7 的最近公共祖 先是 2,1 和 2 的最近公共祖先是 1,6 和 6 的最近公共祖先是 6。

输入输出格式

输入格式:

从标准输入读入数据。 输入第一行,包含一个正整数 m。 输入接下来 m 行,每行包含 2 个正整数,表示 aia_iai​ 和 bib_ibi​ 。

输出格式:

输出到标准输出中。 输入一共 m 行,每行一个正整数,依次表示你对问题的答案。

输入输出样例

输入样例#1: 复制

5

1 1

2 3

5 7

7 13

4 12

输出样例#1: 复制

1

1

2

2

4

说明

【数据范围与约定】 子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解 决一部分测试数据。 每个测试点的数据规模及特点如下表:


特殊性质 1:保证 aia_iai​, bib_ibi​ 均为某一个月出生的兔子中标号最大的一对兔子。例如,对 于前六个月,标号最大的兔子分别是 1, 2, 3, 5, 8, 13。

特殊性质 2:保证 ∣ai−bi∣≤1|a_i-b_i|\le1∣ai​−bi​∣≤1。

 

这题应该略大于D2T1难度,结论题:儿子节点的序号=父亲节点的序号+小于该儿子节点的斐波那契数

所以父亲节点的序号=儿子节点的序号-小于该儿子节点的斐波那契数

这个结论我推了一个小时,真是zz啊

然后可以根据这个结论做LCA,两者同时往上跳找到相同的祖先节点

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#ifdef WIN32
#define LL "%I64d\n"
#else
#define LL "%lld\n"
#endif
using namespace std;
inline ll read()
{
    ll f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

const int maxx=95;
ll f[maxx],m,now;
int main()
{
	f[1]=f[2]=1;
	rep(i,3,maxx)f[i]=f[i-1]+f[i-2];
	//rep(i,1,maxx)cout<<f[i]<<' ';
//	cout<<endl;
	m=read();
	rep(i,1,m){
		ll x=read(),y=read();
		while(x!=y){
			if(x>y)swap(x,y);
			now=lower_bound(f+1,f+1+maxx,y)-f;
			now--;
			if(f[now]==y)now--;
			y-=f[now];//cout<<now<<' '<<f[now]<<' '<<x<<' '<<y<<endl;
		}
		printf(LL,x);
    }
    return 0;
}
			
			



作者:qwerty1125 发表于2017/11/9 15:26:44 原文链接
阅读:1 评论:0 查看评论

Dynamics CRM Developer Extensions提升你的开发效率(web resource篇)

$
0
0

    本篇要向大家推荐一款开发插件,CRM Developer Extensions,github,如果已经在使用这款插件的可以忽略不看,如果不知道的,那绝对会让你兴奋。

    该工具是visual studio的一款插件,目前支持的最高版本是visual studio2015,该款插件目前的最新版本是1.3.4,下载后会看到名叫"CRMDeveloperExtensions_v1.3.4.1.vsix",直接双击安装即可。

    这个工具包含了插件、web资源、报表、解决方案包,本篇先介绍下web资源模块。

    我们都知道CRM页面中的web resource页面是纯文本型的,无法直接在里面进行开发,我们一般都是在本地的IDE中开发完后再把代码拷贝上去发布,如果开发过程中涉及频繁的改动,频繁的发布(修复bug的过程中),且不说代码拷贝很烦,在页面打开发布的一些列等待的过程也是很让人崩溃的,那这款插件就来拯救你的崩溃(当然有人会说不是有官方的depolyment工具吗,说实话那个也不是很好用,谁用谁知道)。

    下面来看下怎么用这款工具,安装成功后打开一个项目,右击项目会看到已经有CRM Developer Extensions的菜单项



   点开后能看到下图几个选项,点击Web Resource Deployer



    我们会看到如下界面,左侧有四个按钮,分别是添加connection、修改connection、删除connection、连接到CRM, 我们首先要添加一个connection,配置我们的CRM环境信息

   

    这种配置界面我们应该看的很多了,提供各种方式的连接,填上后点击connect

    

    

    经过一段时间以后的加载,就能列出CRM中所有web资源,你可以通过solution筛选,加载特定solution下的web资源,也可以在filter项中通过type来筛选。


    

    有个地方要注意下,这里的mappedto要和本地的文件做一个映射,你看到下图中红框中是我本地项目的一个目录结构,为什么要做map是为了后面更快的发布,当然你也可以选择download或者新建web 资源(这里我不做介绍,自己碳探索,很简单)。


    然后进入你本地的文件,开发完成后在文件中右击,或者在项目目录结构的文件上右击,你会看到一个publish to CRM,点一下2到3秒就部署完毕,如果你右击没有发布到CRM的按钮,那就是你前面一步没有做Map。

    



    为了验证是否部署成功了,你可以去看下系统中的web资源库中对应的文件是否改过来了。

    最后要感谢这个插件的作者,向大神Jason Lattimer致敬。 

作者:woniu1104913 发表于2017/11/9 15:31:01 原文链接
阅读:0 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>