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

LLVM每日谈之二十五 Clangd和LSP

$
0
0
1、Clangd
Clang的Extra Clang Tools中有一个ClangD。按照ClangD的定义,它是对Language Server Protocol的一个具体实现(当然是建立在Clang的基础之上的),目的是为了提供编程语言的一些智能化的特性,比如代码完成、引用查找等,主要面向的终端是C/C++的编辑器。
Clangd的官方定义:
Clangd is an implementation of the Language Server Protocol leveraging Clang. Clangd’s goal is to provide language “smartness” features like code completion, find references, etc. for clients such as C/C++ Editors.
2、LSP,Language Server Protocol
Language Server Protocol是一个协议,一个语言服务协议,主要用于编辑器和语言服务器之间进行交互的协议。这个协议的消息主要分为Request、Resonse、Notification三种类型。按照之前Clangd的定义,它就是对该协议的一个具体实现。
关于该协议的详细背景和介绍,wiki地址:https://en.wikipedia.org/wiki/Language_Server_Protocol
关于该协议,还专门有一个网站:http://langserver.org/
3、Clangd和LSP
按照上面的描述,Clangd既然是说对LSP协议的一个实现,那么它应该是夹在Editor和Server之间的一个部分。但是实际上,在http://langserver.org/上,Clangd被划在了Language Servers这一类型里面。仔细分析,也不无道理。Clangd显然不是只对协议进行了实现,而是基于Clang对LSP和Server都进行了实现,只需要前端对接Editor就可以了。
http://langserver.org/上面,也没有进行三个阶段的划分,而都是直接分为了Language Servers和LSP clients两类。猜测一下,应该是没有人专门去做协议的实现,本身意义不大,更多的是Language Servers本身的实现,然后支持协议。或者是Editor本身去实现协议,支持协议直接对接Language。




作者:snsn1984 发表于2017/11/24 9:33:45 原文链接
阅读:40 评论:0 查看评论

《剑指offer》刷题笔记(时间效率):把数组排成最小的数

$
0
0

《剑指offer》刷题笔记(时间效率):把数组排成最小的数



题目描述

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

解题思路

遇到这个题,全排列当然可以做,但是时间复杂度为O(n!)。在这里我们自己定义一个规则,对拼接后的字符串进行比较。

排序规则如下:
- 若ab > ba 则 a 大于 b,
- 若ab < ba 则 a 小于 b,
- 若ab = ba 则 a 等于 b;

根据上述规则,我们需要先将数字转换成字符串再进行比较,因为需要串起来进行比较。比较完之后,按顺序输出即可。

C++版代码实现

class Solution {
public:
    string PrintMinNumber(vector<int> numbers) {
        int len = numbers.size();
        if(len == 0)
            return "";
        sort(numbers.begin(), numbers.end(),cmp);
        string res;
        for(int i=0; i < len; ++i)
            res += to_string(numbers[i]);
        return res;
    }
    static bool cmp(int a, int b){
        string A = to_string(a) + to_string(b);
        string B = to_string(b) + to_string(a);
        return A < B;
    }
};

Python版代码实现

# -*- coding:utf-8 -*-
class Solution:
    def PrintMinNumber(self, numbers):
        # write code here
        if not numbers:
            return ""
        compare = lambda a, b:cmp(str(a) + str(b), str(b) + str(a))
        cur = sorted(numbers, cmp = compare)
        return "".join(str(s) for s in cur)

系列教程持续发布中,欢迎订阅、关注、收藏、评论、点赞哦~~( ̄▽ ̄~)~

完的汪(∪。∪)。。。zzz

作者:u011475210 发表于2017/11/24 9:42:46 原文链接
阅读:32 评论:0 查看评论

Java BitSet 源码解析(1)

$
0
0

参考:

java.util.BitSet

Java BitSet类


查看类 ArrayListremoveIf 方法源码时,发现其使用 BitSet 类来存储待删除的元素下标

之前没有接触过这个类,了解之后发现其在数据查询和存储方面有很大用处


主要内容:

  1. BitSet 浅析
  2. 类变量和常量
  3. 构造器
  4. set
  5. clear -(2)
  6. get
  7. flip - (3)
  8. valueOf
  9. 位运算(and, andNot, or, xor
  10. next
  11. previous - (4)
  12. 判空 / 判断交集
  13. 大小(length, size, cardinality
  14. toByteArraytoLongArray
  15. BitSet 分析
  16. Java 素数查找
  17. 小结

BitSet 浅析

BitSet,顾名思义,它表示一个位集,在每一位上仅有两个选项 - true 或者 false

在实际操作中,如果需要对一堆数据进行某个条件的判断,那么这一类问题都可以使用类 BitSet 来解决


类变量和常量

BitSet 包含的变量和常量如下所示

private long[] words;

在类 BitSet 内部,使用长整型(long)数组 words 来保存位集

/*
 * BitSets are packed into arrays of "words."  Currently a word is
 * a long, which consists of 64 bits, requiring 6 address bits.
 * The choice of word size is determined purely by performance concerns.
 */
private final static int ADDRESS_BITS_PER_WORD = 6;
private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
private final static int BIT_INDEX_MASK = BITS_PER_WORD - 1;

/* Used to shift left or right for a partial word mask */
private static final long WORD_MASK = 0xffffffffffffffffL;

长整型整数占 64 位,进行左右移动时,使用 6 位二进制即可

private transient int wordsInUse = 0;

变量 wordsInUse 表示在数组 words 中已使用的整数个数

private transient boolean sizeIsSticky = false;

变量 sizeIsSticky 表示是否用户指定了数组 words 的大小(该变量在方法 clone() 和序列化方法中有用到

private static final long serialVersionUID = 7997698588986878753L;

暂时还不清楚该常量的用处


构造器

BitSet 提供了 2 个公有构造器和 1 个私有构造器:

public BitSet()
public BitSet(int nbits)
private BitSet(long[] words)

下面介绍两个公有构造器的使用

  • BitSet()

源码如下:

public BitSet() {
    initWords(BITS_PER_WORD);
    sizeIsSticky = false;
}

首先调用函数 initWords,输入参数为常量 BITS_PER_WORD(大小为 64

然后设置变量 sizeIsStickyfalse,即用户没有设定数组 words 大小

  • BitSet(int nbits)

源码如下:

public BitSet(int nbits) {
    // nbits can't be negative; size 0 is OK
    if (nbits < 0)
        throw new NegativeArraySizeException("nbits < 0: " + nbits);

    initWords(nbits);
    sizeIsSticky = true;
}

输入参数 nbits 表示初始化位集的大小

首先判断参数 nbits 是否符合条件,如果不符合,抛出 NegativeArraySizeException 异常

接下来调用函数 initWords,输入参数为 nbits

最后,设置 sizeIsStickytrue,表示用户指定了数组 words 大小

initWords

源码如下:

private void initWords(int nbits) {
    words = new long[wordIndex(nbits-1) + 1];
}

该方法中,为 words 创建长度为 wordIndex(nbits-1)+1 的长整型数组

wordIndex

/**
 * Given a bit index, return word index containing it.
 */
private static int wordIndex(int bitIndex) {
    return bitIndex >> ADDRESS_BITS_PER_WORD;
}

方法 wordIndex 用于计算位集中下标为 bitIndex 的位在数组 words 中的下标


set

BitSet 提供了 4set 函数:

public void set(int bitIndex)
public void set(int bitIndex, boolean value)
public void set(int fromIndex, int toIndex)
public void set(int fromIndex, int toIndex, boolean value)
  • set(int bitIndex)

源码如下:

public void set(int bitIndex) {
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

    int wordIndex = wordIndex(bitIndex);
    expandTo(wordIndex);

    words[wordIndex] |= (1L << bitIndex); // Restores invariants

    checkInvariants();
}

输入参数 bitIndex 表示位集下标

该函数功能是设定位集中下标为 bitIndex 的值为 true

首先判断下标 bitIndex 是否大于 0,如果不是,抛出 IndexOutOfBoundsException 异常

然后定义变量 wordIndex,调用函数 wordIndex(bitIndex) 计算数组 words 中相对应的下标

调用函数 expandTo,输入 wordIndex,确保 words[wordIndex] 不为 null

进行位与操作,设定位集中该下标的值为 true

调用函数 checkInvariants() 检查

  • set(int bitIndex, boolean value)

源码如下:

public void set(int bitIndex, boolean value) {
    if (value)
        set(bitIndex);
    else
        clear(bitIndex);
}

该函数可以用值 value 设定位集中下标为 bitIndex 的位

如果 value = true,调用方法 set(int bitIndex)

如果 value = false,调用方法 clear(bitIndex) 来清除该位值

  • set(int fromIndex, int toIndex)

源码如下:

public void set(int fromIndex, int toIndex) {
    checkRange(fromIndex, toIndex);

    if (fromIndex == toIndex)
        return;

    // Increase capacity if necessary
    int startWordIndex = wordIndex(fromIndex);
    int endWordIndex   = wordIndex(toIndex - 1);
    expandTo(endWordIndex);

    long firstWordMask = WORD_MASK << fromIndex;
    long lastWordMask  = WORD_MASK >>> -toIndex;
    if (startWordIndex == endWordIndex) {
        // Case 1: One word
        words[startWordIndex] |= (firstWordMask & lastWordMask);
    } else {
        // Case 2: Multiple words
        // Handle first word
        words[startWordIndex] |= firstWordMask;

        // Handle intermediate words, if any
        for (int i = startWordIndex+1; i < endWordIndex; i++)
            words[i] = WORD_MASK;

        // Handle last word (restores invariants)
        words[endWordIndex] |= lastWordMask;
    }

    checkInvariants();
}

该函数用于设置位集中范围在 [fromIndex, toIndex) 之间的值为 true

Note:具体设置的位集范围应该是 [fromIndex, toIndex-1]

第一步:调用函数 checkRange 判断输入参数 fromIndextoIndex 是否符合标准

第二步:如果两个参数相等,则返回

第三步:调用函数 wordIndex,将位集下标转换为数组下标,得到变量 startWordIndexendWordIndex;同时调用函数 expandTo,确保数组容量

第四步:计算掩码 firstWordMasklastWordMask;如果设置范围在数组 words 的同一个数中,则利用掩码对该数进行位与操作;如果不在数组 words 的同一个数中,那么对多个数进行位与操作

最后调用函数 checkInvariants 检查类变量是否符合条件

  • set(int fromIndex, int toIndex, boolean value)

源码如下:

public void set(int fromIndex, int toIndex, boolean value) {
    if (value)
        set(fromIndex, toIndex);
    else
        clear(fromIndex, toIndex);
}

同样可以在批量设置中设定具体值

expandTo

源码如下:

private void expandTo(int wordIndex) {
    int wordsRequired = wordIndex+1;
    if (wordsInUse < wordsRequired) {
        ensureCapacity(wordsRequired);
        wordsInUse = wordsRequired;
    }
}

在使用过程中,位集的大小可以按需要增长。为防止输入位集下标超出当前位集,可以调用 expandTo 来扩展数组 words 的大小

Note:输入参数 wordIndex 是数组下标

首先定义变量 wordsRequired 计算需要的最小的数组大小

如果当前数组中已使用的数量 wordsInUse 小于需要的大小,那么可以调用函数 ensureCapacity 确保数组 words 足够,同时设置 wordsInUse 的最新值为 `wordsRequired“

ensureCapacity

源码如下:

private void ensureCapacity(int wordsRequired) {
    if (words.length < wordsRequired) {
        // Allocate larger of doubled size or required size
        int request = Math.max(2 * words.length, wordsRequired);
        words = Arrays.copyOf(words, request);
        sizeIsSticky = false;
    }
} 

输入参数 wordsRequired 表示当前所需的数组大小

此函数功能是确保数组 words 能够满足需求

如果当前数组 words 长度小于 wordsRequired,那么定义变量 request 计算新建数组大小(两倍当前数组长度或者请求的数组大小,取其中最大值),调用函数 Arrays.copyOf 扩展数组 words,同时设置类变量 sizeIsStickyfalse

checkInvariants

源码如下:

private void checkInvariants() {
    assert(wordsInUse == 0 || words[wordsInUse - 1] != 0);
    assert(wordsInUse >= 0 && wordsInUse <= words.length);
    assert(wordsInUse == words.length || words[wordsInUse] == 0);
}

公共方法改变类 BitSet 实例变量后,调用此函数保证类变量符合以下条件

checkRange

源码如下:

private static void checkRange(int fromIndex, int toIndex) {
    if (fromIndex < 0)
        throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);
    if (toIndex < 0)
        throw new IndexOutOfBoundsException("toIndex < 0: " + toIndex);
    if (fromIndex > toIndex)
        throw new IndexOutOfBoundsException("fromIndex: " + fromIndex +
                                            " > toIndex: " + toIndex);
}

检查输入参数 fromIndextoIndex 是否符合条件,即 0 < fromIndex < toIndex,如果不符合,则抛出 IndexOutOfBoundsException 异常

实例测试

下面输入具体参数来验证函数 set(int fromIndex, int toIndex) 的计算过程(假设当前对象位集均为 false

输入参数 fromIndex = 35, toIndex = 148,所以要设置位集范围 [35, 148) 的位值为 true

0 < fromIndex < toIndex,所以符合函数 checkRange 判断条件

变量 startWordIndex = 0, endWordIndex = 2

firstWordMask = WORD_MASK << fromIndex = 0xffffffffffffffffL << 0010 0011(取后 6 位) = WORD_MASK << 35 = 0xfffffff800000000L

lastWordMask = WORD_MASK >>> -toIndex = 0xffffffffffffffffL >>> 1110 1100(取后 6 位) = WORD_MASK >>> 44 = 0x00000000000fffffL

因为 startWordIndex != endWordIndex,所以先计算数组 words 下标 0 的位与操作

words[startWordIndex] |= firstWordMask -> 0x0000000000000000L |= 0xfffffff800000000L = 0xfffffff800000000L

再计算数组下标 1 的位与操作

words[1] |= WORD_MASK -> 0x0000000000000000L |= 0xffffffffffffffffL = 0xffffffffffffffffL

最后计算数组下标 2 的位与操作

words[2] |= lastWordMask -> 0x0000000000000000L |= 0x00000000000fffffL = 0x00000000000fffffL

测试代码如下:

public static void main(String args[]) {
    BitSet bitSet = new BitSet();

    bitSet.set(35, 148);
    System.out.println(bitSet.cardinality());

    long[] res = bitSet.toLongArray();
    for (Long l : res) {
        System.out.println(Long.toBinaryString(l));
    }
}

这里写图片描述

取值范围长度为 toIndex - fromIndex = 148 - 35 = 113

作者:u012005313 发表于2017/11/24 10:26:53 原文链接
阅读:10 评论:0 查看评论

Java BitSet 源码解析(2)

$
0
0

参考:

java.util.BitSet

Java BitSet类


查看类 ArrayListremoveIf 方法源码时,发现其使用 BitSet 类来存储待删除的元素下标

之前没有接触过这个类,了解之后发现其在数据查询和存储方面有很大用处


主要内容:

  1. BitSet 浅析
  2. 类变量和常量
  3. 构造器
  4. set
  5. clear -(2)
  6. get
  7. flip - (3)
  8. valueOf
  9. 位运算(and, andNot, or, xor
  10. next
  11. previous - (4)
  12. 判空 / 判断交集
  13. 大小(length, size, cardinality
  14. toByteArraytoLongArray
  15. BitSet 分析
  16. Java 素数查找
  17. 小结

clear

在前面的 set 方法中,如果设置位值为 false,那么会调用函数 clear

函数 clear 的功能是设置位值为 false,类 BitSet 提供了 3 种重载方式:

public void clear(int bitIndex)
public void clear(int fromIndex, int toIndex)
public void clear()
  • clear(int bitIndex)

源码如下:

public void clear(int bitIndex) {
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

    int wordIndex = wordIndex(bitIndex);
    if (wordIndex >= wordsInUse)
        return;

    words[wordIndex] &= ~(1L << bitIndex);

    recalculateWordsInUse();
    checkInvariants();
}

输入参数 bitIndex 表示位集下标

该函数功能是设置位集中下标为 bitIndex 的位值为 false

第一步:判断参数 bitIndex 是否小于 0,如果成立,抛出 IndexOutOfBoundsException 异常

第二步:定义变量 wordIndex 计算数组 words 中相对应的下标;并判断是否变量 wordIndex 在当前数组已使用个数范围内,如果不是,直接退出(因为默认为 false

第三步:利用位左移运算符和位取反运算符计算掩码,然后对数组中下标 wordIndex 的值进行位与操作,设置该位值为 false

第四步:调用函数 recalculateWordsInUse 重新计算当前数组已使用个数;调用函数 checkInvariants 判断类变量是否符合条件

  • clear(int fromIndex, int toIndex)

源码如下:

public void clear(int fromIndex, int toIndex) {
    checkRange(fromIndex, toIndex);

    if (fromIndex == toIndex)
        return;

    int startWordIndex = wordIndex(fromIndex);
    if (startWordIndex >= wordsInUse)
        return;

    int endWordIndex = wordIndex(toIndex - 1);
    if (endWordIndex >= wordsInUse) {
        toIndex = length();
        endWordIndex = wordsInUse - 1;
    }

    long firstWordMask = WORD_MASK << fromIndex;
    long lastWordMask  = WORD_MASK >>> -toIndex;
    if (startWordIndex == endWordIndex) {
        // Case 1: One word
        words[startWordIndex] &= ~(firstWordMask & lastWordMask);
    } else {
        // Case 2: Multiple words
        // Handle first word
        words[startWordIndex] &= ~firstWordMask;

        // Handle intermediate words, if any
        for (int i = startWordIndex+1; i < endWordIndex; i++)
            words[i] = 0;

        // Handle last word
        words[endWordIndex] &= ~lastWordMask;
    }

    recalculateWordsInUse();
    checkInvariants();
}

该函数用于设置一段范围内的位值为 false

Note:该范围为 [fromIndex, toIndex),不包括下标 toIndex 的位

使用方式和 set(int fromIndex, int toIndex) 类似

第一步:调用函数 checkRange 判断输入参数是否符合条件

第二步:判断输入参数 fromIndextoIndex 是否相等,如果相等,直接退出

第三步:计算数组 words 中的起始下标 startWordIndex,如果该下标已超出当前数组 words 中已使用的个数,那么直接退出

第四步:计算数组 words 中的结束下标 endWordIndex,同样的,如果该下标已超出当前数组 words 中已使用的个数,那么调用函数 length() 计算当前已使用位集长度,赋值给 toIndex,同时设置 endWordIndex 值为数组已使用的最大下标

第五步:计算起始掩码 firstWordMask 和 结束掩码 lastWordMask,将其使用于位操作中

第六步:调用函数 recalculateWordsInUse 重新计算当前数组已使用个数;调用函数 checkInvariants 判断类变量是否符合条件

  • clear()

源码如下:

public void clear() {
    while (wordsInUse > 0)
        words[--wordsInUse] = 0;
}

该函数功能是设置位集所有位为 false

recalculateWordsInUse

源码如下:

private void recalculateWordsInUse() {
    // Traverse the bitset until a used word is found
    int i;
    for (i = wordsInUse-1; i >= 0; i--)
        if (words[i] != 0)
            break;

    wordsInUse = i+1; // The new logical size
}

该函数用于重新计算数组 words 中已使用的个数


get

如果想要获取位值,可以调用函数 get,类 BitSet 提供了 2 种重载方式:

public boolean get(int bitIndex)
public BitSet get(int fromIndex, int toIndex)
  • get(int bitIndex)

源码如下:

public boolean get(int bitIndex) {
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

    checkInvariants();

    int wordIndex = wordIndex(bitIndex);
    return (wordIndex < wordsInUse)
        && ((words[wordIndex] & (1L << bitIndex)) != 0);
}

该函数功能是得到位集中对应下标 bitIndex 的位值

第一步:判断输入参数是否符合条件,以及调用函数 checkInvariants 判断类变量是否符合条件(不太明白为啥要在这里弄这个

第二步:计算位集下标对应的数组下标 wordIndex,进行逻辑与操作,如果下标 wordIndex 超出数组已使用范围,那么直接返回 false;否则,返回该位值

  • get(int fromIndex, int toIndex)

源码如下:

public BitSet get(int fromIndex, int toIndex) {
    checkRange(fromIndex, toIndex);

    checkInvariants();

    int len = length();

    // If no set bits in range return empty bitset
    if (len <= fromIndex || fromIndex == toIndex)
        return new BitSet(0);

    // An optimization
    if (toIndex > len)
        toIndex = len;

    BitSet result = new BitSet(toIndex - fromIndex);
    int targetWords = wordIndex(toIndex - fromIndex - 1) + 1;
    int sourceIndex = wordIndex(fromIndex);
    boolean wordAligned = ((fromIndex & BIT_INDEX_MASK) == 0);

    // Process all words but the last word
    for (int i = 0; i < targetWords - 1; i++, sourceIndex++)
        result.words[i] = wordAligned ? words[sourceIndex] :
            (words[sourceIndex] >>> fromIndex) |
            (words[sourceIndex+1] << -fromIndex);

    // Process the last word
    long lastWordMask = WORD_MASK >>> -toIndex;
    result.words[targetWords - 1] =
        ((toIndex-1) & BIT_INDEX_MASK) < (fromIndex & BIT_INDEX_MASK)
        ? /* straddles source words */
        ((words[sourceIndex] >>> fromIndex) |
         (words[sourceIndex+1] & lastWordMask) << -fromIndex)
        :
        ((words[sourceIndex] & lastWordMask) >>> fromIndex);

    // Set wordsInUse correctly
    result.wordsInUse = targetWords;
    result.recalculateWordsInUse();
    result.checkInvariants();

    return result;
}

该函数返回一个新的 BitSet 对象,用于保存当前位集中范围在 [fromIndex, toIndex) 的位值

第一步:调用函数 checkRange 检查输入参数是否符合条件,同时调用函数 checkInvariants 判断类变量是否符合条件

第二步:定义变量 len,计算位集大小,如果当前位集长度不大于起始下标 fromIndex 或者两个输入参数相等,那么创建一个空的 BitSet 对象并返回;如果终止下标大于当前位集大小,则设置终止下标 toIndex 大小为当前位集大小 len

第三步:创建类 BitSet 对象 result,初始位集大小为待取值的位集范围;定义变量 targetWords,赋值为待取值的位集转换成的长整型整数个数;定义变量 sourceIndex,计算当前数组 words 中对应的起始下标;定义变量 wordAligned,计算起始下标是否是 64 的整数倍

第四步:前面已经计算得出,待取值的位集可转换为 targetWordslong 型整数。先从当前数组 words 中取出前 targetWors-1 个位值,然后再取出剩余部分的位值

第五步:设置 result 的变量并返回

实例测试

下面输入具体参数来验证函数 get(int fromIndex, int toIndex) 的计算过程

首先创建类对象 bitSet,设置位集 [3, 200)true

调用函数 get,输入参数 fromIndex = 5, toIndex = 147

0 < fromIndex < toIndex,所以符合函数 checkRange 判断条件

变量 len = 200,经过判断后,fromIndextoIndex 的值不变

创建类对象 result = new BitSet(142),定义变量 targetWords = wordIndex(147-5-1)+1 = 3,定义变量 sourceIndex = 0,定义变量 wordAligned = false

for 循环如下所示:

for (int i=0; i<2; i++,sourceIndex ++)
    ...

其中

result.words[0] = (words[0] >>> fromIndex) | (words[1] << -fromIndex) 
    = (0xfffffffffffffff8L >>> 00 0101) | (0xffffffffffffffffL << 11 1011) 
    = (words[0] >>> 5) | (words[1] << 59)
    = 0x7fffffffffffffffL | 0xf800000000000000L
    = 0xffffffffffffffffL

result.words[1] = (words[1] >>> fromIndex) | (words[2] << -fromIndex) 
    = (0xffffffffffffffffL >>> 00 0101) | (0xffffffffffffffffL << 11 1011) 
    = (words[0] >>> 5) | (words[1] << 59)
    = 0xffffffffffffffffL

现在处理最后一个 word

定义变量

lastWordMask = WORD_MASK >>> -toIndex = 0xffffffffffffffffL >>> 10 1101 = WORD_MASK >>> 45 = 0x00000000000effffL

(toIndex-1) & BIT_INDEX_MASK) = 146 & 63 = 2^7+2^4+2 & 111111 = 1001 0010 & 0011 1111 = 0001 0010 = 18

fromIndex & BIT_INDEX_MASK = 5 & 63 = 0000 0101 & 0011 1111 = 0000 0101 = 5

所以最后一个值的计算公式为

((words[sourceIndex] & lastWordMask) >>> fromIndex) = (0xffffffffffffffffL & 0x00000000000effffL) >>> 5
 = 0x00000000000effffL >>> 5 = 0x000000000003fffL

完整代码如下:

public static void main(String args[]) {
    BitSet bitSet = new BitSet();

    bitSet.set(3, 200);
    System.out.println(bitSet.cardinality());

    BitSet result = bitSet.get(5, 147);
    System.out.println(result.cardinality());

    long[] res = result.toLongArray();
    for (Long l : res) {
        System.out.println(Long.toHexString(l));
    }
}

这里写图片描述

作者:u012005313 发表于2017/11/24 10:29:09 原文链接
阅读:9 评论:0 查看评论

Java BitSet 源码解析(3)

$
0
0

参考:

java.util.BitSet

Java BitSet类


查看类 ArrayListremoveIf 方法源码时,发现其使用 BitSet 类来存储待删除的元素下标

之前没有接触过这个类,了解之后发现其在数据查询和存储方面有很大用处


主要内容:

  1. BitSet 浅析
  2. 类变量和常量
  3. 构造器
  4. set
  5. clear -(2)
  6. get
  7. flip - (3)
  8. valueOf
  9. 位运算(and, andNot, or, xor
  10. next
  11. previous - (4)
  12. 判空 / 判断交集
  13. 大小(length, size, cardinality
  14. toByteArraytoLongArray
  15. BitSet 分析
  16. Java 素数查找
  17. 小结

flip

如果想要改变位集中的位值(就是从 true 变为 false,或者反之),可以使用函数 flip,类 BitSet 提供了 2 种重载方式

public void flip(int bitIndex)
public void flip(int fromIndex, int toIndex)
  • flip(int bitIndex)

源码如下:

public void flip(int bitIndex) {
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

    int wordIndex = wordIndex(bitIndex);
    expandTo(wordIndex);

    words[wordIndex] ^= (1L << bitIndex);

    recalculateWordsInUse();
    checkInvariants();
}

输入参数为位集下标

该函数功能是改变指定下标的位值

第一步:判断输入参数是否符合条件,否则抛出 IndexOutOfBoundsException 异常

第二步:计算输入位集下标对应的数组下标,并调用函数 expandTo 扩展数组

第三步:使用异或操作,改变对应的位值下标

第四步:调用函数 recalculateWordsInUse 重新计算当前已使用数组大小,调用函数 checkInvariants 判断类变量是否符合条件

  • flip(int fromIndex, int toIndex)

源码如下:

public void flip(int fromIndex, int toIndex) {
    checkRange(fromIndex, toIndex);

    if (fromIndex == toIndex)
        return;

    int startWordIndex = wordIndex(fromIndex);
    int endWordIndex   = wordIndex(toIndex - 1);
    expandTo(endWordIndex);

    long firstWordMask = WORD_MASK << fromIndex;
    long lastWordMask  = WORD_MASK >>> -toIndex;
    if (startWordIndex == endWordIndex) {
        // Case 1: One word
        words[startWordIndex] ^= (firstWordMask & lastWordMask);
    } else {
        // Case 2: Multiple words
        // Handle first word
        words[startWordIndex] ^= firstWordMask;

        // Handle intermediate words, if any
        for (int i = startWordIndex+1; i < endWordIndex; i++)
            words[i] ^= WORD_MASK;

        // Handle last word
        words[endWordIndex] ^= lastWordMask;
    }

    recalculateWordsInUse();
    checkInvariants();
}

该方法功能是改变位集下标为 [fromIndex, toIndex) 的位值

代码和 set(int fromIndex, int toIndex) 类似,不同的是该方法使用的位异或操作


valueOf

BitSet 提供了方法 valueOf,用于解析 long 型数组或者 byte 型数组,将其转换为 BitSet 对象,提供了 4 种重载方式

public static BitSet valueOf(long[] longs)
public static BitSet valueOf(LongBuffer lb)
public static BitSet valueOf(byte[] bytes)
public static BitSet valueOf(ByteBuffer bb)

4 种方法都将调用私有构造器

private BitSet(long[] words)

位运算(and, andNot, or, xor

不同 BitSet 对象之间也可以进行位运算,类 BitSet 提供了 4 种函数

public void and(BitSet set)
public void andNot(BitSet set)
public void or(BitSet set) 
public void xor(BitSet set)
  • and

源码如下:

public void and(BitSet set) {
    if (this == set)
        return;

    while (wordsInUse > set.wordsInUse)
        words[--wordsInUse] = 0;

    // Perform logical AND on words in common
    for (int i = 0; i < wordsInUse; i++)
        words[i] &= set.words[i];

    recalculateWordsInUse();
    checkInvariants();
}

该函数用于对当前对象进行位与操作,仅当当前位集和输入位集中同一下标的位值都为 true时,保留当前位值,否则,设为 false

  • andNot

源码如下:

public void andNot(BitSet set) {
    // Perform logical (a & !b) on words in common
    for (int i = Math.min(wordsInUse, set.wordsInUse) - 1; i >= 0; i--)
        words[i] &= ~set.words[i];

    recalculateWordsInUse();
    checkInvariants();
}

如果输入位集的位值为 true,则设置当前位集中对应下标的位值为 false

  • or

源码如下:

public void or(BitSet set) {
    if (this == set)
        return;

    int wordsInCommon = Math.min(wordsInUse, set.wordsInUse);

    if (wordsInUse < set.wordsInUse) {
        ensureCapacity(set.wordsInUse);
        wordsInUse = set.wordsInUse;
    }

    // Perform logical OR on words in common
    for (int i = 0; i < wordsInCommon; i++)
        words[i] |= set.words[i];

    // Copy any remaining words
    if (wordsInCommon < set.wordsInUse)
        System.arraycopy(set.words, wordsInCommon,
                         words, wordsInCommon,
                         wordsInUse - wordsInCommon);

    // recalculateWordsInUse() is unnecessary
    checkInvariants();
}

对当前位值执行位与操作,只要当前位集和输入位集中有一个位值为 true,则设置当前位集该下标的位值为 true

  • xor

源码如下:

public void xor(BitSet set) {
    int wordsInCommon = Math.min(wordsInUse, set.wordsInUse);

    if (wordsInUse < set.wordsInUse) {
        ensureCapacity(set.wordsInUse);
        wordsInUse = set.wordsInUse;
    }

    // Perform logical XOR on words in common
    for (int i = 0; i < wordsInCommon; i++)
        words[i] ^= set.words[i];

    // Copy any remaining words
    if (wordsInCommon < set.wordsInUse)
        System.arraycopy(set.words, wordsInCommon,
                         words, wordsInCommon,
                         set.wordsInUse - wordsInCommon);

    recalculateWordsInUse();
    checkInvariants();
}

仅当当前位集和输入位集中同一下标的位值相反时,设置当前位集该下标的位值为 true,否则,设为 false


next

BitSet 提供了函数 next 来检索下标,共有两种方式:

public int nextSetBit(int fromIndex)
public int nextClearBit(int fromIndex)
  • nextSetBit(int fromIndex)

源码如下:

public int nextSetBit(int fromIndex) {
    if (fromIndex < 0)
        throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);

    checkInvariants();

    int u = wordIndex(fromIndex);
    if (u >= wordsInUse)
        return -1;

    long word = words[u] & (WORD_MASK << fromIndex);

    while (true) {
        if (word != 0)
            return (u * BITS_PER_WORD) + Long.numberOfTrailingZeros(word);
        if (++u == wordsInUse)
            return -1;
        word = words[u];
    }
}

输入参数 fromIndex 表示位集中开始检索的起始下标,如果没有则抛出 -1

该函数功能是从 fromIndex 开始,检索第一个位值为 true 的下标

第一步:检查输入参数 fromIndex 是否符合条件,不符合则抛出 IndexOutOfBoundsException 异常;调用函数 checkInvariants 检查类参数是否符合条件

第二步:调用函数 wordIndex 转换位下标为数组下标,如果得到的数组下标 u 已经大于等于当前已使用的数组个数,那么直接抛出 -1

第三步:计算掩码,排除前 fromIndex 位的干扰,读取下标为 u 的数组数值进行位与运算,计算结果赋值给变量 word

第四步:如果 word 不等于 0,那么在该长整数上一定存在符合条件的位下标,(u * BITS_PER_WORD) 表示前 u 个整数表示的位长,函数 Long.numberOfTrailingZeros(word) 用于计算长整数中从最低位(从右到左)开始连续位值为 false 的长度;如果 word 等于 0,那么将下标 u 增加 1,首先判断是否超出数组已使用个数,如果超出,返回 -1,否则,将下标为 u 的值赋给 word,继续比较

  • nextClearBit(int fromIndex)

源码如下:

public int nextClearBit(int fromIndex) {
    // Neither spec nor implementation handle bitsets of maximal length.
    // See 4816253.
    if (fromIndex < 0)
        throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);

    checkInvariants();

    int u = wordIndex(fromIndex);
    if (u >= wordsInUse)
        return fromIndex;

    long word = ~words[u] & (WORD_MASK << fromIndex);

    while (true) {
        if (word != 0)
            return (u * BITS_PER_WORD) + Long.numberOfTrailingZeros(word);
        if (++u == wordsInUse)
            return wordsInUse * BITS_PER_WORD;
        word = ~words[u];
    }
}

该函数功能与 nextSetBit 相反,目的在于找出从 fromIndex 开始的第一个位值为 false 的下标

函数实现上和 nextSetBit 相似,虽然是查找位值为 false 的下标,但是在实现时会执行位取反操作,这样,仍旧转换为查找位值为 true 的下标

关键代码如下:

long word = ~words[u] & (WORD_MASK << fromIndex);
...
word = ~words[u];

Long.numberOfTrailingZeros

源码如下:

public static int numberOfTrailingZeros(long i) {
    // HD, Figure 5-14
    int x, y;
    if (i == 0) return 64;
    int n = 63;
    y = (int)i; if (y != 0) { n = n -32; x = y; } else x = (int)(i>>>32);
    y = x <<16; if (y != 0) { n = n -16; x = y; }
    y = x << 8; if (y != 0) { n = n - 8; x = y; }
    y = x << 4; if (y != 0) { n = n - 4; x = y; }
    y = x << 2; if (y != 0) { n = n - 2; x = y; }
    return n - ((x << 1) >>> 31);
}

该函数功能是返回从最低位开始的连续位值为 false 的长度,如果输入参数为 0,那么返回 64

该函数的实现是不断进行位左移运算,每次左移大小为之前的一半(二分法)

实例测试

遍历整个 BitSet 对象,找出位值为 true 的下标

public static void main(String args[]) {
    BitSet bitSet = new BitSet();

    for (int i = 0; i < 10; i += 2) {
        bitSet.set(i);
    }

    for (int i = bitSet.nextSetBit(0); i >= 0; i = bitSet.nextSetBit(i + 1)) {
        if (i == Integer.MAX_VALUE)
            break;
        System.out.println(i);
    }
}

同理,找出位值为 false 的下标仅需将函数 nextSetBit 改为 nextClearBit 即可

作者:u012005313 发表于2017/11/24 10:30:22 原文链接
阅读:23 评论:0 查看评论

Java BitSet 源码解析(4)

$
0
0

参考:

java.util.BitSet

Java BitSet类


查看类 ArrayListremoveIf 方法源码时,发现其使用 BitSet 类来存储待删除的元素下标

之前没有接触过这个类,了解之后发现其在数据查询和存储方面有很大用处


主要内容:

  1. BitSet 浅析
  2. 类变量和常量
  3. 构造器
  4. set
  5. clear -(2)
  6. get
  7. flip - (3)
  8. valueOf
  9. 位运算(and, andNot, or, xor
  10. next
  11. previous - (4)
  12. 判空 / 判断交集
  13. 大小(length, size, cardinality
  14. toByteArraytoLongArray
  15. BitSet 分析
  16. Java 素数查找
  17. 小结

previous

BitSet 提供了函数 previous,用于查找在指定下标之前的最近的位值为 true 或者 false 的下标

public int previousSetBit(int fromIndex) 
public int previousClearBit(int fromIndex)
  • previousSetBit(int fromIndex)

源码如下:

public int previousSetBit(int fromIndex) {
    if (fromIndex < 0) {
        if (fromIndex == -1)
            return -1;
        throw new IndexOutOfBoundsException(
            "fromIndex < -1: " + fromIndex);
    }

    checkInvariants();

    int u = wordIndex(fromIndex);
    if (u >= wordsInUse)
        return length() - 1;

    long word = words[u] & (WORD_MASK >>> -(fromIndex+1));

    while (true) {
        if (word != 0)
            return (u+1) * BITS_PER_WORD - 1 - Long.numberOfLeadingZeros(word);
        if (u-- == 0)
            return -1;
        word = words[u];
    }
}

输入参数 fromIndex 为位集下标

该函数功能是找出位集中在 fromIndex 之前最近的位值为 true 的下标,如果不存在,则返回 true

第一步:参数检查

第二步:得到数组对应下标 u,如果 u 不小于数组已使用下标,表示在它之前离它最近的下标为 位集大小 - 1,即 length()-1

第三步:利用无符号右移运算符,计算掩码,设置 fromIndex 之后位值为 false,与数组下标 u 的数值进行位与操作,得到变量 word

第四步:如果 word 不等于 0,那么表示该整数中存在符合条件的位,否则,判断下标 u 是否已等于 0,如果是,返回 -1,否则,u-1 后继续赋值给 word

  • previousClearBit(int fromIndex)

该函数功能和 previousBitSet 相反,检索在 fromIndex 之前的最近的位值为 false 的下标

函数实现和 previousBitSet 类似,仅是修改了关键实现:

long word = ~words[u] & (WORD_MASK >>> -(fromIndex+1));
    ...    
return (u+1) * BITS_PER_WORD -1 - Long.numberOfLeadingZeros(word);
    ...                
word = ~words[u];

Long.numberOfLeadingZeros

返回从最高位开始位值连续为 0 的长度

实例测试

从后向前遍历输出位值为 true 的下标

public static void main(String args[]) {
    BitSet bitSet = new BitSet();

    for (int i = 0; i < 10; i += 2) {
        bitSet.set(i);
    }

    for (int i = bitSet.length(); i >= 0; i = bitSet.previousSetBit(i - 1)) {
        System.out.println(i);
    }
}

判空 / 判断交集

BitSet 使用函数 isEmpty 判断位集是否为空:

public boolean isEmpty() {
    return wordsInUse == 0;
}

其实现是判断已使用整数个数是否为 0

使用函数 intersects 来判断是否和其它 BitSet 对象有交集

源码如下:

public boolean intersects(BitSet set) {
    for (int i = Math.min(wordsInUse, set.wordsInUse) - 1; i >= 0; i--)
        if ((words[i] & set.words[i]) != 0)
            return true;
    return false;
}

只要当前位集和输入位集在某个位上均为 true,那么判断两者有交集


大小(length, size, cardinality

BitSet 提供了 3 种方式来获取大小

public int length()
public int size()
public int cardinality()
  • length()

源码如下:

public int length() {
    if (wordsInUse == 0)
        return 0;

    return BITS_PER_WORD * (wordsInUse - 1) +
        (BITS_PER_WORD - Long.numberOfLeadingZeros(words[wordsInUse - 1]));
}

该函数返回的是当前已使用的位集长度

  • size()

源码如下:

public int size() {
    return words.length * BITS_PER_WORD;
}

该函数返回的是类 BieSet 内部已申请内存的位长度

  • cardinality()

源码如下:

public int cardinality() {
    int sum = 0;
    for (int i = 0; i < wordsInUse; i++)
        sum += Long.bitCount(words[i]);
    return sum;
}

该函数得到的是位集中位值为 true 的个数


toByteArraytoLongArray

BitSet 提供了 2 种方式来将内部位集转换为数组输出

public long[] toLongArray()
public byte[] toByteArray()
  • toLongArray()

源码如下:

public long[] toLongArray() {
    return Arrays.copyOf(words, wordsInUse);
}

因为类 BeiSet 内部使用 long 型数组保存位集,所以返回已使用数组即可

  • toByteArray()

    public byte[] toByteArray() {
        int n = wordsInUse;
        if (n == 0)
            return new byte[0];
        int len = 8 * (n-1);
        for (long x = words[n - 1]; x != 0; x >>>= 8)
            len++;
        byte[] bytes = new byte[len];
        ByteBuffer bb = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
        for (int i = 0; i < n - 1; i++)
            bb.putLong(words[i]);
        for (long x = words[n - 1]; x != 0; x >>>= 8)
            bb.put((byte) (x & 0xff));
        return bytes;
    }
    

转换为 Byte 数组麻烦一点,因为长整形变量占 64 位,字节变量占 8 位,所以一个长整型变量可以转换为 8 个字节变量


BitSet 分析

理清类 BitSet 源码后,可以发现,它适用于处理二值问题,保存一堆数据是否满足某一条件的结果(true 或者 false

优点如下

  • 按位存储,内存占用空间小
  • 已封装好足够多的操作

缺点如下

  • 线程不安全
  • BitSet 内部是动态增加 long 型数组,但是并没有动态减少,可能会引发 OutOfMemory

以下是相关的博文:

为什么Java中的BitSet使用long数组做内部存储,而不使用int数组或者byte数组?

对java的BitSet的多线程并发的探索

java的BitSet使用不当引发OutOfMemory


Java 素数查找

参考:

素数

筛选法

为什么要用BitSet

筛选法求素数

  • 素数

素数(prime number),又称为质数,定义为在大于 1 的自然数中,除了 1 和它本身外不再有其它因数

基本判断思路:

对正整数 n,如果用 2 到根号 n 之间的所有整数去除,均无法整除,则 n 为质数

  • 筛选法求素数(使用 BitSet

第一步:设置位集下标 2-n 均为 true

第二步:从前到后进行遍历,找出位值为 true 的下标,每找到一个,将其与后面下标值进行除法操作,如果能够整除,则设置该下标为 false

操作 1:遍历到 根号 n 即可

操作 2:得到当前下标后,将其用 1,2,3,4... 顺序相乘得到目标下标值

实例测试

找出前 2000万 个整数中包含的素数个数

方法一:

public static int findPrimeNumber_v1(int range) {
    int count = 0;
    boolean flag = false;
    for (int i = 2; i < range; i++) {
        if (i == 2) {
            count++;
            continue;
        }
        flag = true;
        for (int j = 2; j <= Math.sqrt(i); j++) {
            if (i % j == 0) {
                flag = false;
                break;
            }
        }
        if (flag) {
            count++;
        }
    }

    return count;
}

方法二:

public static int findPrimeNumber_v2(int range) {
    BitSet bitSet = new BitSet(range + 1);
    for (int i = 2; i < range; i++) {
        bitSet.set(i);
    }

    int s = (int) Math.sqrt(range);
    int r = 0;

    for (int i = bitSet.nextSetBit(0); i >= 0; i = bitSet.nextSetBit(i + 1)) {
        for (int j = 2; j < range; j++) {
            r = i * j;
            if (r > range)
                break;
            bitSet.clear(r);
        }
    }

    return bitSet.cardinality();
}

测试:

public static void main(String args[]) {
    int range = 20000000;

    long time1 = System.currentTimeMillis();
    System.out.println(findPrimeNumber_v1(range));
    long time2 = System.currentTimeMillis();
    System.out.println(time2 - time1);

    time1 = System.currentTimeMillis();
    System.out.println(findPrimeNumber_v2(range));
    time2 = System.currentTimeMillis();
    System.out.println(time2 - time1);
}

这里写图片描述

由结果可知,使用 BitSet 配合筛选法,查找速度大大增加


小结

这是第一次完整的分析一个类的源码,还没有找到方法更好的理清函数之间的的关系,所以就直接按照函数源码进行了分析(其实有些函数的源码其实并不需要讲解,看一看就明白了

以后应该不会像这样子直接分析源码了,应该在弄懂一个类 / 框架的原理的前提下,再进行归纳总结

作者:u012005313 发表于2017/11/24 10:34:27 原文链接
阅读:26 评论:0 查看评论

android 自定义ProgressBar 文字跟随进度效果

$
0
0

mark

1 字体适配


    private void textSizeAdaptive() {
        //1.获取当前设备的屏幕大小
        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        //2.计算与你开发时设定的屏幕大小的纵横比(这里假设你开发时定的屏幕大小是480*800)
        int screenWidth = displayMetrics.widthPixels;
        int screenHeight = displayMetrics.heightPixels;
        float ratioWidth = (float) screenWidth / 1080;
        float ratioHeight = (float) screenHeight / 1920;

        ratio = Math.min(ratioWidth, ratioHeight);
        if (ratioWidth != ratioHeight) {
            if (ratio == ratioWidth) {
                offsetLeft = 0;
                offsetTop = Math.round((screenHeight - 1920 * ratio) / 2);
            } else {
                offsetLeft = Math.round((screenWidth - 1080 * ratio) / 2);
                offsetTop = 0;
            }
        }
        //3.根据上一步计算出来的最小纵横比来确定字体的大小(假定在1080*1920屏幕下字体大小设定为35)
        TEXT_SIZE = Math.round(textsize * ratio);
    }
  1. onDraw
@Override
    protected synchronized void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        canvas.save();
        mText = (getProgress() * 100 / getMax()) + "%";
        Rect rect = new Rect();
        mPaint.getTextBounds(leftText, 0, leftText.length(), rect);
        int y = (getHeight() / 2) - rect.centerY();

        //在进度条上画上自定义文本
        canvas.drawText(leftText, 5, y, mPaint);

        int width = rect.width();

        //进度
        float radio = getProgress() * 1.0f / getMax();
        float progressPosX = (int) (mRealWidth * radio);

        //起始点
        int beginX = 10 + width;
        //结束点
        float textWidth = mPaint.measureText(mText);
        float endX = mRealWidth - textWidth;
        if (beginX > progressPosX- textWidth) {
            canvas.drawText(mText, beginX, y, mPaint);
        } else if (progressPosX- textWidth > endX) {
            canvas.drawText(mText, endX, y, mPaint);
        } else {
            canvas.drawText(mText, progressPosX - textWidth, y, mPaint);
        }
        canvas.restore();
    }

下载地址 http://download.csdn.net/download/liudao7994/10130885

作者:liudao7994 发表于2017/11/24 10:05:25 原文链接
阅读:1 评论:0 查看评论

《剑指offer》刷题笔记(时间空间效率的平衡):丑数

$
0
0

《剑指offer》刷题笔记(时间空间效率的平衡):丑数



题目描述

把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

解题思路

同样的,我们首先想到的可能就是遍历判断,但是每个数都要计算一次是不是ugly,很是麻烦。于是我们想,能不能只对ugly进行计算呢,显然是可以的。

我们用一个数组来从小到大存放ugly,那现在的问题就是排序问题了,我们如何来保障这里面的数组是排好序的呢?

我们把当前数组里面最大的ugly记为M,那么,下一个ugly肯定是前面的数与2或者3或者5的乘积中的最小值。然后我们拿这个最小值和M进行比较,如果小于M,就说明已经存在于数组中,如果大于M,则说明需要添加进数组。需要注意的是,我们每次判断之后,我们只需要第一个比M大的值,其他的以后会重新计算。程序中通过t2、t3、t5分别记录上次计算用到的ugly的相应序号。

比如现在res中存放的是[1,2,3],此时,t2、t3都为1,t5为0,min(res[1] * 2, min(res[1] * 3, res[1] * 5)) 结果为4,res变为[1,2,3,4],判断后t2变成2,以此类推。

因为7之前的数(7除外)都是ugly,所以程序一开始的判断可以和i直接写index < 7。

C++版代码实现

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if(index < 7)
            return index;
        vector<int> res(index);
        res[0] = 1;
        int t2=0, t3=0, t5=0;
        for(int i=1; i < index; ++i){
            res[i] = min(res[t2] * 2, min(res[t3] * 3, res[t5] * 5));
            if(res[i] == res[t2] * 2)
                t2++;
            if(res[i] == res[t3] * 3)
                t3++;
            if(res[i] == res[t5] * 5)
                t5++;
        }
        return res[index - 1];
    }
};

Python版代码实现

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index < 7:
            return index;
        res = [1]
        t2 = t3 = t5 = 0
        for i in range(1,index):
            res.append(min(res[t2] * 2, min(res[t3] * 3, res[t5] * 5)))
            if res[i] == res[t2] * 2:
                t2 += 1
            if res[i] == res[t3] * 3:
                t3 += 1
            if res[i] == res[t5] * 5:
                t5 += 1
        return res[-1]

系列教程持续发布中,欢迎订阅、关注、收藏、评论、点赞哦~~( ̄▽ ̄~)~

完的汪(∪。∪)。。。zzz

作者:u011475210 发表于2017/11/24 11:08:23 原文链接
阅读:0 评论:0 查看评论

[TensorFlow学习手记] 5 - Regression

$
0
0

这里写图片描述


Code

import tensorflow as tf 
import matplotlib.pyplot as plt 
import numpy as np 

'''
Add layer
定义添加神经层的函数def add_layer(),它有四个参数:输入值、输入的大小、输出的大小和激励函数,
我们设定默认的激励函数是None
'''

def add_layer(inputs, in_size, out_size, activation_function=None):
    Weights = tf.Variable(tf.random_normal([in_size,out_size])) # Normal distribution 生成随机变量矩阵
    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1) # biases初始值不推荐为0
    Wx_plus_b = tf.matmul(inputs,Weights) + biases

    if activation_function is None:
        outputs = Wx_plus_b
    else:
        outputs = activation_function(Wx_plus_b)
    return outputs

x_data = np.linspace(-1,1,300)[:, np.newaxis]

'''
np.newaxis的功能是插入新维度,可以看出np.newaxis分别是在行或列上增加维度,
原来是(10,)的数组,在行上增加维度变成(1,10)的二维数组,在列上增加维度变为(10,1)的二维数组
看下面的例子:

In [1]: np.linspace(1, 10, 10)
Out[1]: array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])

In [2]: np.linspace(1, 10, 10)[np.newaxis,:]
Out[2]: array([[ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]])

In [3]: np.linspace(1, 10, 10)[:,np.newaxis]
Out[3]:
array([[ 1.],
[ 2.],
[ 3.],
[ 4.],
[ 5.],
[ 6.],
[ 7.],
[ 8.],
[ 9.],
[ 10.]])

In [4]: np.linspace(1, 10, 10).shape
Out[4]: (10,)

In [5]: np.linspace(1, 10, 10)[np.newaxis,:].shape
Out[5]: (1, 10)

In [6]: np.linspace(1, 10, 10)[:,np.newaxis].shape
Out[6]: (10, 1)

'''

noise = np.random.normal(0,0.05,x_data.shape) # 增加噪声点
y_data = np.square(x_data) - 0.5 + noise # x^2 + 0.5 +noise

# define placeholder for inputs to network
xs = tf.placeholder(tf.float32, [None,1])
ys = tf.placeholder(tf.float32, [None,1])
'''
利用占位符定义我们所需的神经网络的输入。 tf.placeholder()就是代表占位符,
这里的None代表无论输入有多少都可以,因为输入只有一个特征,所以这里是1'''
l1 = add_layer(xs, 1, 10, activation_function=tf.nn.relu) # 添加隐藏层
predition = add_layer(l1, 10, 1, activation_function=None) # 添加输出层

loss = tf.reduce_mean(tf.reduce_sum(tf.square(ys - predition), reduction_indices=[1]))
'''
tf.reduce_*(
    input_tensor, 输入
    axis=None, # 取哪一维度
    keep_dims=False, # 保持维度
    name=None,
    reduction_indices=None # 兼容
)
'''

train_step = tf.train.GradientDescentOptimizer(0.1).minimize(loss)

init = tf.global_variables_initializer()

sess = tf.Session()
sess.run(init)

for i in range(1000):
    # training
    sess.run(train_step, feed_dict={xs: x_data, ys: y_data})
    if i % 50 == 0:
        # to see the step improvement
        print(sess.run(loss, feed_dict={xs: x_data, ys: y_data}))

Result

1.09957
0.00971825
0.00745628
0.00676008
0.00616613
0.00563508
0.00525119
0.004969
0.00473265
0.00450678
0.00431545
0.00412944
0.00399298
0.00388178
0.00376811
0.00367137
0.00359963
0.00353908
0.00348795
0.00344684
作者:soulmeetliang 发表于2017/11/24 11:54:12 原文链接
阅读:56 评论:0 查看评论

Qt--容器模板类

$
0
0

Qt中提供的容器模板类类似于STL,它提供了Java-style iterators and STL-style iterators两种风格的迭代器,此外还提供了foreach关键字。

Container Classes

sequential containers:

  • QVector<T>
    QVector的内存模型是预先分配好大小的连续数组,所以可以通过索引快速访问,但如果在头部或者中间插入删除需要大量移动数据,效率很慢,此外如果添加超过预设大小,会引起内存重新分配,将原先内存中的数据全部拷贝过来
  • QList<T>
    QList虽然也是通过连续内存来保存T数据,当它是通过数组保存T指针来指向实际在内存中的位置,所以也可以通过索引访问,插入删除速度亦非常快速,Qt中大量使用了QList,可见其便利性和速度结合的不错。
  • QLinkedList<T>
    QLinkedList内存模型就是双向链表了,只能通过迭代访问,链表的特性就是插入删除无限制咯
  • QStack<T>
    QStack是QVector的子类,是后进先出的数据模型,只在结尾插入,结尾删除,所以继承自QVector效率最好了
  • QQueue<T>
    QQueue是QList的子类,是先进先出的数据模型,主要在尾部插入,头部取值删除,所以继承自QList也是不二之选

associative containers:

  • QSet<T>
    QSet是没有重复元素的集合,它以未指定的顺序存储值,并提供非常快速的值查找。QSet内部是基于QHash实现的
  • QMap<Key, T>
  • QMultiMap<Key, T>
  • QHash<Key, T>
  • QMultiHash<Key, T>

QMap和QHash提供非常类似的功能,都提供了键值对的快速查找。差异在于:

  • QMap基于红-黑树的字典,QHash基于哈希表
  • QHash比QMap更快的查找速度
  • 在对QHash进行迭代时,项是任意排序的;在QMap中,项是按键排序的
  • QHash的关键类型必须提供运算符==()和全局QHash(key)函数;QMap的关键类型必须提供操作符<(),以指定排序规则。

QMultiMap和QMultiHash则提供了一对多的映射。

Iterator Classes

Java-style iterators

Read-only iterator是在类名后面加上Iterator,如QListIterator<T>
Read-write iterator,形式如QMutableListIterator<T>

提供的接口有:

  • toFront()
    Moves the iterator to the front of the list (before the first item)
  • toBack()
    Moves the iterator to the back of the list (after the last item)
  • hasNext()
    Returns true if the iterator isn’t at the back of the list
  • next()
    Returns the next item and advances the iterator by one position
  • peekNext()
    Returns the next item without moving the iterator
  • hasPrevious()
    Returns true if the iterator isn’t at the front of the list
  • previous()
    Returns the previous item and moves the iterator back by one position
  • peekPrevious()
    Returns the previous item without moving the iterator

*此处我是拷贝的Qt帮助文档,并没有翻译,因为我觉得程序员必须具备看英文文档的能力

STL-Style Iterators

形式类似于
QList::const_iterator (Read-only iterator )
QList::iterator (Read-write iterator)

提供了运算符操作:

  • *i
    Returns the current item
  • ++i
    Advances the iterator to the next item
  • i += n
    Advances the iterator by n items
  • –i
    Moves the iterator back by one item
  • i -= n
    Moves the iterator back by n items
  • i - j
    Returns the number of items between iterators i and j

foreach

foreach实际是一个宏定义

#  ifndef foreach
#    define foreach Q_FOREACH
#  endif

#define Q_FOREACH(variable, container)                                \
for (QForeachContainer<typename QtPrivate::remove_reference<decltype(container)>::type> _container_((container)); \
     _container_.control && _container_.i != _container_.e;         \
     ++_container_.i, _container_.control ^= 1)                     \
    for (variable = *_container_.i; _container_.control; _container_.control = 0)

形式上是双层for循环,实际第二层for只会循环一次,因为第三条语句container.control = 0必将导致判断条件container.control不生效,那么使用双层for循环,仅仅是为了将*_container_.i赋值给variable。

使用示例:

  QList<QString> list;
  ...
  QString str;
  foreach (str, list)
      qDebug() << str;
作者:GG_SiMiDa 发表于2017/11/24 11:58:06 原文链接
阅读:58 评论:0 查看评论

Leetcode——682. Baseball Game(模拟)

$
0
0

题目链接
Description:
You’re now a baseball game point recorder.

Given a list of strings, each string can be one of the 4 following types:

Integer (one round’s score): Directly represents the number of points you get in this round.
“+” (one round’s score): Represents that the points you get in this round are the sum of the last two valid round’s points.
“D” (one round’s score): Represents that the points you get in this round are the doubled data of the last valid round’s points.
“C” (an operation, which isn’t a round’s score): Represents the last valid round’s points you get were invalid and should be removed.
Each round’s operation is permanent and could have an impact on the round before and the round after.

You need to return the sum of the points you could get in all the rounds.

Example 1:
Input: [“5”,”2”,”C”,”D”,”+”]
Output: 30
Explanation:
Round 1: You could get 5 points. The sum is: 5.
Round 2: You could get 2 points. The sum is: 7.
Operation 1: The round 2’s data was invalid. The sum is: 5.
Round 3: You could get 10 points (the round 2’s data has been removed). The sum is: 15.
Round 4: You could get 5 + 10 = 15 points. The sum is: 30.
Example 2:
Input: [“5”,”-2”,”4”,”C”,”D”,”9”,”+”,”+”]
Output: 27
Explanation:
Round 1: You could get 5 points. The sum is: 5.
Round 2: You could get -2 points. The sum is: 3.
Round 3: You could get 4 points. The sum is: 7.
Operation 1: The round 3’s data is invalid. The sum is: 3.
Round 4: You could get -4 points (the round 3’s data has been removed). The sum is: -1.
Round 5: You could get 9 points. The sum is: 8.
Round 6: You could get -4 + 9 = 5 points. The sum is 13.
Round 7: You could get 9 + 5 = 14 points. The sum is 27.
Note:
The size of the input list will be between 1 and 1000.
Every integer represented in the list will be between -30000 and 30000.

解题:
比较直观的一道模拟题,根据各种要求,计算分数,因为要查看之前的分数,又涉及除去无效操作,可以用stack维护,也可以用vector+标记维护。题目中没有直接点出的一点是,取消上一轮的操作的操作,不算做一个轮次。string.c_str()返回const char*类型(可读不可改)的指向字符数组的指针。atoi()可以将字符数组转换为整数。

代码:

class Solution {
public:
    int calPoints(vector<string>& ops) {
        int res=0,tmp1,tmp2,tmp3;
        stack <int> q;
        for(int i=0;i<ops.size();i++)
        {
            if(ops[i]=="+")
            {
                tmp2=q.top();
                q.pop();
                tmp1=q.top();
                tmp3=tmp1+tmp2;
                res+=tmp3;
                q.push(tmp2);
                q.push(tmp3);
            }
            else if(ops[i]=="D")
            {
                tmp1=q.top();
                tmp2=2*tmp1;
                res+=tmp2;
                q.push(tmp2);
            }
            else if(ops[i]=="C")
            {
                tmp1=q.top();
                q.pop();
                res-=tmp1;
            }
            else
            {
                tmp1=atoi(ops[i].c_str());
                res+=tmp1;
                q.push(tmp1);
            }
        }
        return res;
    }
};
作者:David_Jett 发表于2017/11/24 12:29:40 原文链接
阅读:46 评论:0 查看评论

MyBatis的级联查询(association 进行分布查询)

$
0
0

MyBatis的级联查询(分布查询)

<!--  使用 association 进行分布查询
	    1  先按照员工id查询员工信息
	    2 根据查询员工信息中的d_id的值去部门表查出部门信息
	    3 部门设置到员工中
	    
	 -->



EmployeeMapperPlus.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cn.mybatis.dao.EmployeeMapperPlus">

	
	<!--  使用 association 进行分布查询
	    1  先按照员工id查询员工信息
	    2 根据查询员工信息中的d_id的值去部门表查出部门信息
	    3 部门设置到员工中
	    
	 -->
	
	<resultMap type="com.cn.zhu.bean.Employee" id="MyEmpByStep">
		<id column="id" property="id"/>
		<result column="last_name" property="lastName"/>
		<result column="email" property="email"/>
		<result column="gender" property="gender"/>
		<!-- association 定义管理对象的封装规则
		   select: 表明当前属性是调用select 指定的方法
		   colum: 指定将哪一列的值传给这个方法
		   流程 : 使用select 指定的方法(传入column指定的这列参数的值)查出对象
		   并封装给property 指定的属性
		 -->
		<association property="dept" select="com.cn.mybatis.dao.DepartmentMapper.getDeptById"
		 column="d_id">
		 
		</association>
	</resultMap>
	<!-- public Employee getEmpByIdStep(Integer id); -->
	<select id="getEmpByIdStep" resultMap="MyEmpByStep">
           	select * from tbl_employee  where id=#{id}
 	</select>
 	<!-- 分布好处
 	     Employee==>Dept
 	          我们每次查询Employee对象的时候,都将一起查询出来。
 	          部门信息在我们使用的时候再去查询
 	          分段查询的基础之上加上两个配置
 	 -->
 	 
</mapper>

DepartmentMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cn.mybatis.dao.DepartmentMapper">
	<!-- public Department getDeptById(Integer id); -->
	<select id="getDeptById" resultType="com.cn.zhu.bean.Department">
		select id,dept_name
		departmentName from tbl_dept where id=#{id}
    </select>
  </mapper>

DepartmentMapper.java

package com.cn.mybatis.dao;

import java.util.List;
import java.util.Map;


import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;

import com.cn.zhu.bean.Department;
import com.cn.zhu.bean.Employee;

public interface DepartmentMapper {
	public Department getDeptById(Integer id);
	public Department getDeptByIdPlus(Integer id);
	
	public Department getDeptByIdStep(Integer id);
	public List<Employee> getEmpsByDeptId(Integer deptId);
 }
   
Department.java

package com.cn.zhu.bean;

import java.util.List;

public class Department {
	private 	Integer  id;
	private  String  departmentName;
	private List<Employee> emps;
	
	
	
	public List<Employee> getEmps() {
		return emps;
	}
	public void setEmps(List<Employee> emps) {
		this.emps = emps;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getDepartmentName() {
		return departmentName;
	}
	public void setDepartmentName(String departmentName) {
		this.departmentName = departmentName;
	}
	@Override
	public String toString() {
		return "Department [departmentName=" + departmentName + ", id=" + id
				+ "]";
	}
	
}

EmployeeMapperPlus.java

package com.cn.mybatis.dao;

import java.util.List;
import java.util.Map;


import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;

import com.cn.zhu.bean.Employee;

public interface EmployeeMapperPlus {
	public Employee getEmpById(Integer id);
	public Employee getEmpAndDept(Integer id);
	public Employee getEmpByIdStep(Integer id);
 }
在config.xml加上
mybatis-config.xml

<mappers>
		<mapper resource="mybatis/mapper/EmployeeMapperPlus.xml" />
		<mapper resource="mybatis/mapper/DepartmentMapper.xml" />
	</mappers>

MyBatisTest.java
package com.cn.zhu.mybatis.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import com.cn.mybatis.dao.CopyOfEmployeeMapperAnnotation;
import com.cn.mybatis.dao.DepartmentMapper;
import com.cn.mybatis.dao.EmployeeMapper;
import com.cn.mybatis.dao.EmployeeMapperPlus;
import com.cn.zhu.bean.Department;
import com.cn.zhu.bean.Employee;

/**
 * 1 接口式编程
 *   原生       dao  ====>  DaoImpI
 *   mybatis  Mapper====>xxMapper.xml
 *   
 * 2 Sql Session 代表 和数据库的一次回话,用完必须关闭
 * 3 SqlSession 和  connection 一样她都是非线程安全。
 * @author Administrator
 *
 */
public class MyBatisTest {
	public SqlSessionFactory getSqlSessionFactory() throws IOException{
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		return  new SqlSessionFactoryBuilder().build(inputStream);
	}
	/**
	 * 1. 根据xml配置文件(	全局配置文件)创建一个sqlsessionFactory对象
	 *       有数据源一些运行环境信息
	 * 2  sql映射文件: 配置了每一个sql,以及sql的	封装规则等
	 * 3  将sql映射文件注册在全局配置文件中
	 * 4  写代码
	 *       1 根据全局配置文件得到sqlSessionFactory
	 *       2 使用sqlsession工厂,获取到sqlsession对象使用池来执行增删改查
	 *        一个sqlsession就是代表和数据库的一次回话。用完也要关闭
	 * @throws IOException
	 */
	/*	@Test
	public void test() throws IOException{

	 *//**
	 * 2 获取sqlsession实例,能直接执行已经映射的sql语句
	 *//*
		SqlSession openSession=getSqlSessionFactory().openSession();
		try{
			Employee employee=	openSession.selectOne("com.cn.mybatis.EmployeeMapper.selectEmp", 1);
			System.out.println(employee);		
		}finally{		
			openSession.close();
		}

	}*/
	
	
	@Test
	public void test05() throws IOException{
		SqlSessionFactory sqlsessionFactory=getSqlSessionFactory();
		// 1  获取到的sqlsession不会自动提交数据
		SqlSession openSession=sqlsessionFactory.openSession();

		try{
			
			EmployeeMapperPlus mapper=openSession.getMapper(EmployeeMapperPlus.class);
          //级联查询 
			Employee empAndDept=mapper.getEmpAndDept(1);
            System.out.println(empAndDept);
            System.out.println(empAndDept.getDept());
			// 分布查询
			Employee  employee=mapper.getEmpByIdStep(1);
			System.out.println(employee);
			System.out.println(employee.getDept());
			
		}finally{
			openSession.commit();
		}
	}
	
}

测试结果




接下来会写mybatis对集合的遍历查询


作者:zhupengqq 发表于2017/11/24 12:35:45 原文链接
阅读:50 评论:0 查看评论

MyBatis的分布查询延迟加载(select_resultMap)

$
0
0

MyBatis的分布查询延迟加载(select_resultMap)

	<!-- 分布好处  可以使用延迟加载
 	     Employee==>Dept
 	          我们每次查询Employee对象的时候,都将一起查询出来。
 	          部门信息在我们使用的时候再去查询
 	          分段查询的基础之上加上两个配置
 	 -->

mybatis-config.xml  改变配置文件

	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true" />  //驼峰命名法
		<!-- 懒加载 ,延迟加载-->
		<setting name="lazyLoadingEnabled" value="true"/> 
		<!-- 立即加载 -->
		<setting name="aggressiveLazyLoading" value="false"/>
	</settings>
测试方法
@Test
	public void test05() throws IOException{
		SqlSessionFactory sqlsessionFactory=getSqlSessionFactory();
		// 1  获取到的sqlsession不会自动提交数据
		SqlSession openSession=sqlsessionFactory.openSession();

		try{

			EmployeeMapperPlus mapper=openSession.getMapper(EmployeeMapperPlus.class);
			//级联查询 
			/*	Employee empAndDept=mapper.getEmpAndDept(1);
            System.out.println(empAndDept.getLastName());*/
			//System.out.println(empAndDept.getDept());
			// 分布查询
			Employee  employee=mapper.getEmpByIdStep(1);
			System.out.println(employee.getLastName());
			//System.out.println(employee.getDept());

		}finally{
			openSession.commit();
		}
	}
在开启延迟加载的情况下

<setting name="lazyLoadingEnabled" value="true"/> 



在没有开启延迟加载的情况,立即加载

<setting name="aggressiveLazyLoading" value="false"/>

所以好处就是

<!-- 分布好处  可以使用延迟加载
 	     Employee==>Dept
 	          我们每次查询Employee对象的时候,都将一起查询出来。
 	          部门信息在我们使用的时候再去查询
 	          分段查询的基础之上加上两个配置
 	 -->

作者:zhupengqq 发表于2017/11/24 12:59:05 原文链接
阅读:59 评论:0 查看评论

BZOJ1834: [ZJOI2010]network 网络扩容

$
0
0

Description

给定一张有向图,每条边都有一个容量C和一个扩容费用W。这里扩容费用是指将容量扩大1所需的费用。求: 1、 在不扩容的情况下,1到N的最大流; 2、 将1到N的最大流增加K所需的最小扩容费用。
Input

输入文件的第一行包含三个整数N,M,K,表示有向图的点数、边数以及所需要增加的流量。 接下来的M行每行包含四个整数u,v,C,W,表示一条从u到v,容量为C,扩容费用为W的边。
Output

输出文件一行包含两个整数,分别表示问题1和问题2的答案。
Sample Input

5 8 2

1 2 5 8

2 5 9 9

5 1 6 2

5 1 1 8

1 2 8 7

2 5 4 9

1 2 1 1

1 4 2 1

Sample Output

13 19

30%的数据中,N<=100

100%的数据中,N<=1000,M<=5000,K<=10

题目传送门

第一个问就挺简单的,跑一遍裸的dicnic即可
但是第二个问怎么搞??重新建图吗??
我想了想,否决了,因为其实可以在残图上直接跑费用流
对于每一条边,我们建一条新边(原来的费用为零),如果还能跑,就说明这个点还有流量可以跑,就不用补费用,反之,则要跑补出来的费用,就可以解决了

然后我费用流打错是怎么回事??

代码如下:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct node{int x,y,c,d,next,other;}a[211000];int len,last[110000];
void ins(int x,int y,int c,int d)
{
    int k1,k2;
    k1=++len;
    a[len].x=x;a[len].y=y;a[len].c=c;a[len].d=d;
    a[len].next=last[x];last[x]=len;

    k2=++len;
    a[len].x=y;a[len].y=x;a[len].c=0;a[len].d=-d;
    a[len].next=last[y];last[y]=len;

    a[k1].other=k2;a[k2].other=k1;
}
int list[11000];
int head,tail,st,ed;
int n,m,K;
int h[11000];
bool bt_h()
{
    head=1;tail=2;
    memset(h,0,sizeof(h));
    h[st]=1;list[1]=st;
    while(head!=tail)
    {
        int x=list[head];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(h[y]==0 && a[k].c>0)
            {
                h[y]=h[x]+1;
                list[tail++]=y;
            }
        }
        head++;
    }
    if(h[ed]==0)return false;
    return true;
}
int find_flow(int x,int f)
{
    if(x==ed)return f;
    int s=0,t;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(h[y]==h[x]+1 && a[k].c>0 && s<f)
        {
            s+=(t=find_flow(y,min(a[k].c,f-s)));
            a[k].c-=t;a[a[k].other].c+=t;
        }
    }
    if(s==0)h[x]=0;
    return s;
}
int pos[21000],t[21000];
int d[21000],v[21000];
bool spfa()
{
    for(int i=1;i<=n+1;i++)d[i]=999999999;
    d[st]=0;
    memset(v,false,sizeof(v));v[st]=true;
    list[1]=st;head=1;tail=2;
    while(head!=tail)
    {
        int x=list[head];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(a[k].c>0 && d[y]>d[x]+a[k].d)
            {
                pos[y]=x;t[y]=k;
                d[y]=d[x]+a[k].d;
                if(v[y]==false)
                {
                    v[y]=true;
                    list[tail++]=y;
                    if(tail==n+2)tail=1;
                }
            }
        }
        head++;
        if(head==n+2)head=1;
        v[x]=false;
    }
    if(d[n+1]==999999999)return false;
    return true;
}
int p[21000],q[21000],g[21000],co[21000];
int main()
{
    scanf("%d%d%d",&n,&m,&K);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&p[i],&q[i],&g[i],&co[i]);
        ins(p[i],q[i],g[i],0);
    }
    st=1;ed=n;
    int ans=0;
    while(bt_h())ans+=find_flow(st,999999999);
    printf("%d ",ans);
    for(int i=1;i<=m;i++)ins(p[i],q[i],K,co[i]);
    ins(n,n+1,K,0);
    ans=0;
    while(spfa())
    {
        int tp=n+1,minn=999999999;
        while(tp!=st)
        {
            minn=min(minn,a[t[tp]].c);
            tp=pos[tp];
        }
        tp=n+1;
        while(tp!=st)
        {
            ans+=minn*a[t[tp]].d;
            a[t[tp]].c-=minn;a[a[t[tp]].other].c+=minn;
            tp=pos[tp];
        }
    }
    printf("%d\n",ans);
    return 0;
}

by_lmy

作者:HeroDeathes 发表于2017/11/24 13:14:50 原文链接
阅读:25 评论:0 查看评论

TensorFlow on Android:物体识别

$
0
0

[运营专题]零预算引爆个人和企业品牌
Selenium 自动化测试从零实战
原来这样做,才能向架构师靠近
Cordova App 打包全揭秘
TensorFlow on Android:物体识别
图解敏捷教练和 ScrumMaster

说在前面:达人课是GitChat的一款轻阅读产品,由特约讲师独家发布。每一个课程你都可获得6-12篇的深度文章,同时可在读者圈与讲师互动交流。GitChat达人课,让技术分享更简单。进入我的GitChat

这里写图片描述

作者介绍

付强,十余年从业经验,从 C 语言到 Web 开发,从微服务架构到移动端开发,涉猎范围很广。曾就职于趋势科技、诺基亚,在德国和硅谷的 Startups 工作过,现在从事物联网方向的创业。

课程介绍

本课程讲解如何在 Android 上面运行 TensorFlow,并利用基于 TensorFlow 的 Object Detection API 来实现一个可以识别静态图片中的物体的应用,并通过该过程让没有任何机器学习基础的读者了解机器学习在移动端的使用场景和流程,包括如何加载模型、如何准备输入数据、如何解析推理结果。完成本课程以后,还可以学习其他应用的课程。查看原文

第01课:机器学习在移动应用现状

深度学习、神经网络、人工智能应该是当下最火爆的字眼了,随着 AlphaGo 的一炮走红,仿佛人人都谈论着人工智能,说话不带 DL、CNN 这些字眼的就落伍了。

各大巨头也纷纷在 AI 领域布局,目前比较流行的深度学习框架有:

  • 谷歌的开源深度学习框架 TensorFlow
  • Facebook 的开源深度学习框架 Torchnet
  • 百度的开源深度学习框架 Paddle
  • 源自伯克利的 Caffe
  • 基于 Theano/TensorFlow 的 Keras
  • ……

当大家还在讨论人工智能、机器学习前景的时候,有几件有趣的事情发生了:

  1. Facebook 在 2016 年的时候发布了 Caffe2go,移动端的深度学习库;
  2. 2017年 Google IO 上面发布了 TensorFlow Lite,移动端的神经网络库;
  3. 腾讯优图开源了 ncnn,实现深度神经网络在移动端的落地。

等一下,读者可能会感觉到有个问题:什么, 在移动端进行机器学习?机器学习不是应该需要海量的数据和计算资源吗(CPU/GPU)?移动设备的存储和计算能力能满足要求吗?在回答这个问题之前,来看看下面这一个场景:你需要完成一个人脸检测的 App 应用,但是你们公司没有机器学习的专家,怎么办?

当然,这个难不倒大家,在这个时代最不缺的就是轮子了,各大平台都有开放的 API 共大家调用,比如:

  • Google Vision API
  • 腾讯优图
  • 百度人脸识别 API
  • Face++
  • 讯飞云平台人脸相关 API
  • ….

使用开放的 API 有很多优点,比如:

  1. 不需要任何机器的学习的基础,对于小团队来说能够快速实现产品;
  2. 使用简单,只需要通过 HTTP 接口将图片和相关参数上送到服务器,就能马上得到结果;
  3. 能够及时的享受到大平台的海量数据和计算资源对识别模型的优化和修正。

但是也有明显的缺点:

  1. 需要移动端能访问互联网,上传图片需要消耗流量较大,在弱网络环境下,特别是对一些物联网设备的限制比较大;
  2. API 调用需要付费,特别是为了实现 SLA 更需要付出额外的成本;
  3. 数据安全存在隐患,特别是人脸这样的敏感数据;
  4. 虽然云平台有海量计算资源,但是对于单独的 API 调用来讲,仍然存在一定时延,没法做到实时检测。

现在让我们换一种思路来解决这个问题,以目前移动设备的存储和计算能力,是不可能实现在移动端进行模型训练的,当然也没有这个必要。 但是如果在服务端利用海量的数据和计算资源训练好模型,然后将训练好的模型部署到移动端,只利用移动端的计算能力来进行推理(Inference,记住这个术语,当和机器学习专家讨论的时候用到这个词,他们就会认为你是懂行的),可行吗?

答案是当然可行了, 而且随着移动端计算能力的提升,这个将是机器学习在移动端落地的趋势,这也是各大巨头正在做的事情。


开始学习

坦白地说,机器学习的学习曲线是比较陡的,首先需要有一些编程知识和线性代数的基础;然后要学习一些算法并推导它们,SVM、线性回归、聚类算法等,学会如何评估学习结果、学会梯度下降的推导、学会各种提取特征的方法、PCA等;接着再去学习神经网络的概念、学会各种网络、如卷积神经网络等;开始准备数据,训练你自己的模型,最后再调上非常长时间的各种参数….

机器学习是一个工具,一个可以用来解决现实问题的工具。如果想成为一个机器学习专家/科学家,上面的过程是必须的,因为你要成为一个制作工具的人。 但是等等,假设只是一个想使用机器学习这个工具来解决现实问题的普通工程师,该怎么办?

回想一下当时是怎么从零学会 Web 编程的:

  1. 选一个框架,如 SSH、Rails、Django 等,照着模板依葫芦画瓢做一个项目;

  2. 有了整体的概念以后再去优化一下代码,学会一些 best practice;

  3. 研究框架,如读读实现代码;

  4. 研究 HTTP 协议,寻找可以调优的地方;

  5. 尝试写一个框架或者插件;

  6. 最后成为 Web 编程的专家。

本课程正是遵循上述的流程,首先我们提一个现实问题,然后用开源的框架和模型来解决它,在这个过程中会了解机器学习的一些基本概念和流程(这样就可以在喝咖啡的时候和机器学习专家谈笑风声了),然后学会如何使用这种工具。在本系列课程的后面,还将学习如何从头训练一个属于你的独一无二的模型,将学习如何优化这个工具。最后再倒过头来去研究和推导神经网络的算法,剩下的就看你的了!

要解决的问题

先看一张图:

enter image description here

这个监控是不是很厉害?但是它的实现原理很简单的(过程依然很复杂):

  1. 用大量的带标记(图片上的是什么物体、处于什么位置)的图片数据来训练一个模型;
  2. 用这个模型来识别视频每一帧中的物体(人、汽车等);
  3. 将识别结果可视化(在物体周围画上边框和标签)。
    我们要做的是这个功能的第一步,在 Android 上面选取一张图片,识别图片中有哪些物体,并将识别结果可视化。完成了静态图片的识别,再扩展到实时识别视频中的每一帧就是非常简单的了。

接下来我们就撸起袖子开始吧~

下一篇

课程内容

第01课:机器学习在移动应用现状

第02课:选择工具和资源

第03课:Demo 展示和准备工作

第04课:输入数据预处理和 Inference

第05课:可视化推理结果

第06课:看起来像个机器学习专家

作者:GitChat 发表于2017/11/24 10:04:28 原文链接
阅读:28 评论:0 查看评论

TensorFlow on Android:训练模型

$
0
0

文章推荐

[运营专题]零预算引爆个人和企业品牌
Selenium 自动化测试从零实战
原来这样做,才能向架构师靠近
Cordova App 打包全揭秘
TensorFlow on Android:物体识别
图解敏捷教练和 ScrumMaster

说在前面:达人课是GitChat的一款轻阅读产品,由特约讲师独家发布。每一个课程你都可获得6-12篇的深度文章,同时可在读者圈与讲师互动交流。GitChat达人课,让技术分享更简单。进入我的GitChat

温馨提示:阅读本文章建议先学习《TensorFlow on Android:物体识别》

这里写图片描述

作者介绍

付强,十余年从业经验,从 C 语言到 Web 开发,从微服务架构到移动端开发,涉猎范围很广。曾就职于趋势科技、诺基亚,在德国和硅谷的 Startups 工作过,现在从事物联网方向的创业。

课程介绍

本课程是《TensorFlow on Android:物体识别》的后续课程,紧接上一课程内容,来讲解如何基于 TensorFlow 的 Object Detection API 训练一个新的物体识别模型,在这个过程中会讲解机器学习和深度学习的一些知识和概念。通过本课程的学习,将获得机器学习从训练到应用的 hands-on 的经验,包括 data labeling、深度学习环境搭建、可视化学习过程等,从而达到让零基础机器学习的读者能够快速高效的解决实际问题的目的。

第01课:训练一个独一无二的模型

大家好,很高兴能在这里继续 TensorFlow On Android 的课程。

《TensorFlow on Android:物体识别》中我们已经学会了如何使用一个事先训练好的 pre-trained model 来识别图片中的多种物体,这很好、也很酷,即使没有任何机器学习基础也可以做出一个相当专业的应用了,也许有的读者已经把静态图片识别移植到了视频监控上面,开始监控大门口的人流了呢!

注意:建议先学习《TensorFlow on Android:物体识别》这门课,也可以先学习本课再学上一门课。因为先训练再应用是一个正常的流程,而在这里先讲应用再讲训练只是遵循由简到繁的原则,总之按照自己的情况自由选择就好了。

通过上一门课程的学习,相信给大家打开了一扇新世界的大门。就像之前开发 Web 应用的时候,可以从开源社区找到很多轮子,而不需要重新开发,在机器学习的世界里,也可以从开源社区找到很多框架、算法、模型等工具,我们也学习了去哪里找这些工具、如何学习使用这些工具。

但是如果只是使用事先训练好的模型,在实际工作中,往往还不能真正解决问题。以物体识别为例,如果要识别的物体,如熊猫,不在这 pre-trained 模型训练的范围里,怎么办?我们需要训练一个独一无二、属于自己的模型。

其实大部分开源的模型都公开了其训练的代码,所以可以通过这些代码加上我们自己的数据来进行训练,从而获得一个满足自己业务需求的模型。本课程中将通过使用 Object Detection API 训练一个可以识别熊猫的模型来讲解整个流程,将要学习的东西包括:

  • 机器学习/深度学习的一些重要概念和术语,本门课程面向没有任何机器学习基础的读者,所以会用尽量简单和直白的语言来描述。
  • TensorFlow-GPU 的安装和环境配置。
  • 准备训练数据和 data-labeling。
  • 可视化学习过程。
  • 导出学习结果。

注意:有些厂商号称开源了某某模型,但是实际上并没有公开其训练部分的代码,这种其实算不上开源,因为没有办法定制这个模型。

通过本课程的学习,我们将补齐机器学习中关于训练部分的知识,这样就可以定制这个工具来解决真正的问题了,当完成了这一步以后,再去看深度学习的算法推导或者 TensorFlow 的示例代码,应该会容易不少,因为知其然,再知其所以然,这应该是一个较优的学习过程。回想一下在学习 Web 编程、iOS 编程等的时候,是不是也是这样一个过程?

在本课程中,我们需要写一些 Python 代码,所以必须了解一些 Python 的知识,不过,不需要成为 Python 高手,只需要了解变量和函数声明、模块导入的知识就足够了。如果英文比较好,可以直接参考 官方的教程,如果喜欢在中文的环境里面学习,可以参考 Python 教程 - 廖雪峰的官方网站

本课程使用 Python 的版本为 2.7,请事先安装和配置好。

接下来我们先学习一些机器学习的知识和术语,在后面的实际操作中会用的到这些知识和术语。

下一篇

课程内容

第01课:训练一个独一无二的模型

第02课:一些必须了解的机器学习知识

第03课:运行一个 Demo

第04课:在 GPU 上进行训练

第05课:准备训练数据

第06课:熊猫识别器

作者:GitChat 发表于2017/11/24 10:39:17 原文链接
阅读:14 评论:0 查看评论

iOS解决NSArray、NSDictionary打印乱码问题

$
0
0

原来打印

dict = {
key1 = abc;
key2 = "\U4e2d\U6587";
} 

array = (
abc,
"\U4e2d\U6587"
)

使用方法:将 NSArray+Extension 和 NSDictionary+Extension 两个分类拖入项目即可

打印效果 :

2015-08-08 10:19:36.294 ICUnicodeDemo[2135:50801]

dict = {
key1 = abc,
key2 = 中文
} 


array = [
abc,
中文
]

附主要代码文件:
NSArray+Extension.h

#import <Foundation/Foundation.h>

@interface NSArray (Extension)

@end

NSArray+Extension.m

#import "NSArray+Extension.h"

@implementation NSArray (Extension)



-(NSString *)descriptionWithLocale:(id)locale
{
    NSMutableString *msr = [NSMutableString string];
    [msr appendString:@"["];
    for (id obj in self) {
        [msr appendFormat:@"\n\t%@,",obj];
    }
    //去掉最后一个逗号(,)
    if ([msr hasSuffix:@","]) {
        NSString *str = [msr substringToIndex:msr.length - 1];
        msr = [NSMutableString stringWithString:str];
    }
    [msr appendString:@"\n]"];
    return msr;
}


@end

NSDictionary+Extension.h


#import <Foundation/Foundation.h>

@interface NSDictionary (Extension)

@end

NSDictionary+Extension.m

#import "NSDictionary+Extension.h"

@implementation NSDictionary (Extension)
-(NSString *)descriptionWithLocale:(id)locale
{
    NSMutableString *msr = [NSMutableString string];
    [msr appendString:@"{"];
    [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        [msr appendFormat:@"\n\t%@ = %@,",key,obj];
    }];
    //去掉最后一个逗号(,)
    if ([msr hasSuffix:@","]) {
        NSString *str = [msr substringToIndex:msr.length - 1];
        msr = [NSMutableString stringWithString:str];
    }
    [msr appendString:@"\n}"];
    return msr;
}
@end

ViewController.m


#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //创建数组
    NSArray *array = @[@"abc",@"中文"];
    //创建字典
    NSDictionary *dict = @{@"key1" : @"abc",
                           @"key2" : @"中文",
                           };
    //打印数组和字典
    NSLog(@" \n dict = %@ \n array = %@",dict,array);

}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
作者:gsg8709 发表于2017/11/24 14:16:12 原文链接
阅读:7 评论:0 查看评论

Go实战--golang中使用echo框架中JSONP(labstack/echo)

$
0
0

生命不止,继续 go go go !!!

继续,echo web框架,今天就聊一聊JSONP。

JSONP

1、什么是JSONP?

JSONP (JSON with padding) is used to request data from a server residing in a different domain than the client. It was proposed by Bob Ippolito in 2005.

JSONP enables sharing of data bypassing same-origin policy. The policy disallows running JavaScript to read media DOM elements or XHR data fetched from outside the page’s origin. The aggregation of the site’s scheme, port number and host name identifies as its origin. Due to inherent insecurities, JSONP is being replaced by CORS.

关于golang中cors可以参考:Go实战–golang中使用echo框架中的cors(labstack/echo、rs/cors)

要了解JSONP,不得不提一下JSON,那么什么是JSON ?

JSON is a subset of the object literal notation of JavaScript. Since JSON is a subset of JavaScript, it can be used in the language with no muss or fuss.

关于golang中使用json可以参考:
Go语言学习之encoding/json包(The way to go)
Go实战–net/http中JSON的使用(The way to go)

JSONP(JSON with Padding)是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。

JSONP有什么用
由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出JSON数据并执行回调函数,从而解决了跨域的数据请求。

如何使用JSONP
下边这一DEMO实际上是JSONP的简单表现形式,在客户端声明回调函数之后,客户端通过script标签向服务器跨域请求数据,然后服务端返回相应的数据并动态执行回调函数。

Jsonp原理
首先在客户端注册一个callback, 然后把callback的名字传给服务器。

此时,服务器先生成 json 数据。
然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp.

最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。

客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)

JS中使用JSONP

html

<button id="btn">Click to get HTTP header via JSONP</button>
<pre id="result"></pre>

JavaScript
有人提供的线上的服务,通过jsonp返回http请求:
https://ajaxhttpheaders.appspot.com/?callback={{YOUR_CALLBACK_NAME}}

'use strict';

var btn = document.getElementById("btn");
var result = document.getElementById("result");

function myCallback(acptlang) {
  result.innerHTML = JSON.stringify(acptlang, null, 2);
}

function jsonp() {
  result.innerHTML = "Loading ...";
  var tag = document.createElement("script");
  tag.src = "https://ajaxhttpheaders.appspot.com/?callback=myCallback";
  document.querySelector("head").appendChild(tag);
}

btn.addEventListener("click", jsonp);

golang中实现JSONP

参考地址:
https://siongui.github.io/2017/03/18/go-jsonp-server-implementation-code/

要点:
Get the name of front-end callback function from the query string in HTTP request.

Encode the data to be returned to the client in JSON format. In this case, it’s the HTTP headers encoded in JSON.

Set the Content-Type as application/javascript in HTTP response.

Return the string callbackName(JSONString); to the client, where callbackName is the name of callback in query string, and JSONString is the JSON-encoded data in step 2.

main.go

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

const indexHtml = `<!DOCTYPE html>
<html>
<head><title>Go JSONP Server</title></head>
<body>
<button id="btn">Click to get HTTP header via JSONP</button>
<pre id="result"></pre>
<script>
'use strict';

var btn = document.getElementById("btn");
var result = document.getElementById("result");

function myCallback(acptlang) {
  result.innerHTML = JSON.stringify(acptlang, null, 2);
}

function jsonp() {
  result.innerHTML = "Loading ...";
  var tag = document.createElement("script");
  tag.src = "/jsonp?callback=myCallback";
  document.querySelector("head").appendChild(tag);
}

btn.addEventListener("click", jsonp);
</script>
</body>
</html>`

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, indexHtml)
}

func jsonpHandler(w http.ResponseWriter, r *http.Request) {
    callbackName := r.URL.Query().Get("callback")
    if callbackName == "" {
        fmt.Fprintf(w, "Please give callback name in query string")
        return
    }

    b, err := json.Marshal(r.Header)
    if err != nil {
        fmt.Fprintf(w, "json encode error")
        return
    }

    w.Header().Set("Content-Type", "application/javascript")
    fmt.Fprintf(w, "%s(%s);", callbackName, b)
}

func main() {
    http.HandleFunc("/jsonp", jsonpHandler)
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8000", nil)
}

浏览器输入:
http://localhost:8000/

点击按钮,结果:

{
  "Accept": [
    "*/*"
  ],
  "Accept-Encoding": [
    "gzip, deflate, br"
  ],
  "Accept-Language": [
    "zh-CN,zh;q=0.9,en;q=0.8"
  ],
  "Alexatoolbar-Alx_ns_ph": [
    "AlexaToolbar/alx-4.0"
  ],
  "Connection": [
    "keep-alive"
  ],
  "Cookie": [
    "_ga=GA1.1.981224509.1509938615"
  ],
  "Referer": [
    "http://localhost:8000/"
  ],
  "User-Agent": [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
  ]
}

echo中使用JSONP

参考地址:
https://echo.labstack.com/cookbook/jsonp

html

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <title>JSONP</title>
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    <script type="text/javascript">
        var host_prefix = 'http://localhost:1323';
        $(document).ready(function() {
            // JSONP version - add 'callback=?' to the URL - fetch the JSONP response to the request
            $("#jsonp-button").click(function(e) {
                e.preventDefault();
                // The only difference on the client end is the addition of 'callback=?' to the URL
                var url = host_prefix + '/jsonp?callback=?';
                $.getJSON(url, function(jsonp) {
                    console.log(jsonp);
                    $("#jsonp-response").html(JSON.stringify(jsonp, null, 2));
                });
            });
        });
    </script>

</head>

<body>
    <div class="container" style="margin-top: 50px;">
        <input type="button" class="btn btn-primary btn-lg" id="jsonp-button" value="Get JSONP response">
        <p>
            <pre id="jsonp-response"></pre>
        </p>
    </div>
</body>

</html>

main.go

package main

import (
    "math/rand"
    "net/http"
    "time"

    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

func main() {
    e := echo.New()
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    e.Static("/", "public")

    // JSONP
    e.GET("/jsonp", func(c echo.Context) error {
        callback := c.QueryParam("callback")
        var content struct {
            Response  string    `json:"response"`
            Timestamp time.Time `json:"timestamp"`
            Random    int       `json:"random"`
        }
        content.Response = "Sent via JSONP"
        content.Timestamp = time.Now().UTC()
        content.Random = rand.Intn(1000)
        return c.JSONP(http.StatusOK, callback, &content)
    })

    // Start server
    e.Logger.Fatal(e.Start(":1323"))
}

这里写图片描述

作者:wangshubo1989 发表于2017/11/24 15:27:15 原文链接
阅读:109 评论:0 查看评论

【GitChat】精选——JavaScript进阶指南

$
0
0

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

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

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

从这里发表我的Chat


本周Chat专题推荐

JavaScript 作为 GitHub 上排名第一的热门语言,摩尔定律已经不足以描述 JS 技术发展的速度了。本专题将助你提升自己的 JS 功力,从而从容面对层出不穷的前端黑科技。

这里写图片描述

本专题包含以下活动:

本专题包含以下活动,请务必添加GitChat服务号以查看活动进度及获取活动通知。

订阅本专题Chat


本周Chat推荐:

Web 渗透测试入门

这里写图片描述

笔者从事信息安全相关工作多年,长期进行风险评估、等保测评、渗透测试等项目的实施,有丰富的实战经验。专注于研究 Web 安全、熟知常见 Web 安全漏洞;目前任职安全研究员。通过本场 Chat 我将分享以下内容:

  • 渗透测试基本环境搭建
  • 网站基础与信息搜集
  • 信息搜集、端口扫描

订阅本场Chat


机器学习入门第一课:从高中课本谈起

这里写图片描述

从简单的高中课本上的线性回归和最小二乘法谈起,解秘线性回归采用最小二乘法的奥秘,揭示最优化问题与概率统计的本质关系。

一线互联网工程师与你一起走进奇妙的算法世界,无论你是刚上大学的学生,还是准备校招的应届生,亦或是工作多年,准备转行 AI 的朋友们,都可以学习到有用的知识。

订阅本场Chat


如何设计出高可用、高性能的接口

这里写图片描述

设计接口是一件容易的事,也是件困难的事。设计接口每个人都会,每个人都能设计,也由此产生了各种各样的理念的接口。工作这么多年,我也很有感悟。很多人会说,设计接口多么简单,只要命名好,然后联调通了,上线可以调用就行了。特别是非互联网行业的人,这里没有歧视的意思。因为互联网行业和传统行业太多不一致性决定了这种思想的产生。

接口是项目里面的最小粒度的单元,接口设计需要注意点很多,需要的考虑方方面面,很多人也不重视,而且设计接口需要的技术栈也需要很多,能充分考察到技术人的知识的广度以及深度。下面介绍的是我工作中的一些感悟,希望能与诸位共同交流,探讨。本场 Chat 主要包含以下三个方面:

  • 接口设计需要考虑哪些方面
  • 接口设计原则
  • 如何保证接口的高可用、高性能

订阅本场Chat


组织敏捷转型中的 HR

这里写图片描述

在组织敏捷转型过程中,团队应该怎么做、管理者应该怎么做,这在社区和业界实践中已经积累了大量的案例和讨论。但是研发团队之外的角色,职能部门应该如何参与到敏捷转型中,以及在敏捷组织中这些角色和部门应该如何工作,这是一个较少被谈及的话题。

在本场 Chat 中,我将结合一些案例分析,探讨在敏捷转型各个阶段中,HR 的职能发生什么变化、如何助力研发团队、如何推动组织转型更加顺利地进行。

订阅本场Chat


成为 GitChat 超级会员,价格低至 99 元

这里写图片描述

从小白到大牛,经历不平凡! 全年 Chat 免费订 · 优质资源任意享 · 领域专家畅快聊 · 精品课程随心学

了解更多GitChat会员


作者:blogdevteam 发表于2017/11/24 15:48:13 原文链接
阅读:7 评论:0 查看评论

IOS中NSArray的4种遍历方式

$
0
0

objective-c 语言 数组遍历的4种方式:1、普通for循环;2、快速for循环;3、特性block方法;4、枚举方法。

一. for循环

Student *stu = [Student student];  
NSArray *array = [NSArray arrayWithObjects:stu, @"1",@"2",nil];  
int count = array.count;//减少调用次数  
for( int i=0; i<count; i++){  
    NSLog(@"%i-%@", i, [array objectAtIndex:i]);  
}  

二. 增强for

for(id obj in array){  
    NSLog(@"%@",obj);  
}  

三. 迭代器

NSEnumerator *enumerator = [array objectEnumerator];  
id obj = nil;  
while(obj = [enumerator nextObject]){  
    NSLog(@"obj=%@",obj);  
} 

四. Block块遍历

[array enumeratorObjectsUsingBlock:  
^(id obj, NSUInteger index, BOOL  *stop){  
    NSLog(@"%i-%@",index,obj);  
    //若终断循环  
    *stop = YES;  
}];  
作者:gsg8709 发表于2017/11/24 15:55:16 原文链接
阅读:0 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


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