Showing posts with label to-do. Show all posts
Showing posts with label to-do. Show all posts

Monday, November 7, 2016

How to Solve It: Modern Heuristics



http://blog.csdn.net/feliciafay/article/details/6200509
@@问题=>模型=>求解
求解问题的时候,我们其实往往是先把问题描述为一个模型,然后给问题的模型求解

@@证明问题的基本思路
1演绎法
2归纳法
3反证法
4把这个问题转换为它的逆否命题再做证明

@@对算法进行分类
第一类:评估子空间而非个体解
贪婪算法、动态规划、分枝界定法
第二类:将整个搜索空间看成潜在解的统一集合,只评估单个完整解,而没有子空间的概念。
        爬山法、模拟退火、禁忌搜索
第三类:演化算法
       结合以上两类的特点,允许个体既描述子空间,又描述特定解

@@候选解的组织形式
SAT问题可以用树的形式来组织,这样可以方便剪枝等操作。

@@如何设计评估函数?
评估函数可以比目标函数更加简单,这样可以提高效率。

@@对参数调整进行分类
1)运行前的参数调整
2)运行时的参数调整
         2.1)确定性的
         2.2)适应的
         2.3)自适应的


@@关于演化算法
1 由所解决任务的候选解构成一个种群,通过随机变化和选择等一代代演化下去。其中随机变化提供了发现新解的机制,而选择则确定了保持哪些解作为下一步搜索的基础。
2 如果没有办法快速找到完美解,那么应该退而求其次,尝试快速产生近似最优解。


一些疑惑存于心中,我接下来会把以下问题弄清楚。
1什么样的问题适合动态规划?
2模拟退火与禁忌搜索的异同比较
3书中谈假设检验时用了下面的说法:我们即使有再多的证据也不能说“接受”零假设,我们只能说不拒绝零假设。
这样的表述反映了假设检验的怎样的实质?我需要好好研究一下。
http://blog.csdn.net/feliciafay/article/details/6203923
如果说这本书的主菜是对启发式算法的介绍,那么开胃酒就是点缀其间的小问题了。如果把这些每个小问题整理整理,形成若干小主题,那么将会非常有意思。

主题一 扩大你的搜索空间
1 卓上有六根火柴棒,请搭建四个三角形。
问题的形式暗示这是一个二维的平面,这个形式误导了我们,使我们限制于此空间,难以求解。
 
然而,如果改变搜索空间。比如,假设是在三维空间中,六根火柴就可以完成搭建四个三角形的任务。
 


2 过河修桥问题
如下图所示,我们需要在城市A与城市B之间修建座桥,但是两个城市被河流给分隔开。我们需要使这两个城市之间的道路的总路程最小,且桥必须建得和河水垂直,该如何修桥?
 

常规思路,列式表达出AB之间的道路的距离,计算量很繁琐。
换一种想法,求AB之间的道路的距离的时候,我们其结果发现和桥的位置虽然相关,但是最后其实并没有包含桥的长度。这样,问题可以被转换为一个常见问题。
想象河流的宽度逐渐缩短到零,那么AB间的最短距离就是线段AB。现在,河流的宽度虽不为零,但可以把B点上移桥宽的距离至新的B’点,这样AB’就是要求的最短距离了,而AB’和桥岸的交点就是河道修建的位置了。

 


3 台球的运动轨迹
假设有个台球被撞击了一下,然后无限地在桌面上运动了起来,它遵循物理学的一个基本定律,角α总是等于角β,那么,在什么情况下台球会在桌面上做往返运动?

假设台球只反射1次,我们很容易作出到如上图所示的图画,然而,如果台球反射10次,我们的思维就会乱掉。

如果扩展问题的空间,问题会简单许多。当球反射第一次的时候,其实以下两幅图是等价的。
 
顺这个思路继续,每当球进行了一次反射,我们就扩展一次桌面,那么最后将出现如下图所示的结果:
也就是说,如果球能够回到原始位置,说明它将形成如上图所示的造型,
也就是说,经过水平方向的p个桌子和竖直方向的q个桌子。
也就是说,tan(α)=p/q,即tan(α)是个有理数。


主题二 不同的假设带来不同的结论
大前研一在《思考的技术》中强调的最核心的一句话便是:解答问题是有模型的,这个模型就是“假设-结论”模型。

这个模型提出的思维框架非常有力。它把你已有的一条条杂乱的思维线索以更明晰的方式组织了起来。虽然你的单独一条思维线索不能通过这个框架得到飞跃,但是它们之间相互构建的方式会得到改变。我认为这就是这个思维框架的优势。它没有花费大量时间成本去提升你的思维能力,它只用少量时间把原有的思维重新组合了一下却收效明显。这个模型很棒。

那么具体是什么模型呢?

常见的思维误区太渴望得到一个解答而忘记了其实可能有不止一个解答。
往往,解答可以有很多个。比发现一个解答更棒的,是发现一堆解答,比发现一堆解答更棒的,是发现在怎样的假设下会发生这样的解答。

案例来了,地面上有个半径为1的圆R,有个像两个方向无限延长的直线L丢在地上将圆分为两半。要求出该圆被切出的切弦的长度大于等于√3概率。

一方面,把切弦的中点叫做M,从上图可以看出来,如果中点M落在半径为R/2的小圆的范围内,那么切弦的长度小于√3,如果中点M落在圆环(大圆扣除小圆所形成的圆环)的范围内,那么切弦的长度大于等于√3。所以,所求的概率等于小圆的面积与圆环的面积的比值1/4。
另一方面,想象这条直线首先和圆相切,然后逐步向圆心靠近直到刚好切弦长度为√3,此时圆心距离直线R/2。也就是说,所求的概率等于R/2与R的比值1/2。
现在,发现这两个值居然不相等,是否有某个值计算出错?
其实,两个值不相等的真正原因是我们在求每个解的时候已经默认了随机线段的运动模型。对于第一个解,默认随机线段的中点在圆中是均匀分布的,对于第二个解,默认随机线段总是朝着一个方向运动而不发生旋转。
总结一下,这个问题真正的解决要点在于首先要识别出可能存在的假设,然后还要理解不同的假设如何对应不同的结论。

Tuesday, May 10, 2016

SimpleArrayMap



https://segmentfault.com/a/1190000005052408
https://github.com/android/platform_frameworks_support/blob/master/v4/java/android/support/v4/util/SimpleArrayMap.java
SimpleArrayMap跟v4包中的ArrayMap最大的区别,证明就是ArrayMap继承了SimpleArrayMap,又实现了Map的接口;主要的操作,则是通过引入MapCollections类,使用Map中的Entry结构,这样在ArrayMap中就可以通过Iterator来进行数据的的迭代操作。
  • 思想:SimpleArrayMap采用了两个数组来进行hash值与key、value值得保存,另外,数组大小超过8时,并需要进行扩容时,只增大当前数组大小的一半,并对大小为4和8的数组进行缓存。这样最后带来的好处就是最大程度保证了数组空间都能够被使用,一定程度上避免了内存空间的浪费。
  • 数据结构方式:使用了两个数组,一个是Hash数组,另一个是大小*2的Array数组,为了保证通用性,这里所使用的是Object数组。Array数组中使用key+value间隔存取的方式,偶数为即0 -> key1 1 -> value1 2 -> key2 3 -> value2 。另外Hash数组,则是对应的Key的Hash值数组,并且这是一个有序的int数组,这样在进行Key的查找时,使用二分查找则是最有效率的方式
int[] mHashes;
Object[] mArray;
int mSize;
代码中,mHashes数组为mArray中的key对应的hash值得数组,而mArray即是HashMap中key与value间隔混合的一个数组。
public void clear() {
   if (mSize != 0) {
      freeArrays(mHashes, mArray, mSize);
      mHashes = ContainerHelpers.EMPTY_INTS;
      mArray = ContainerHelpers.EMPTY_OBJECTS;
      mSize = 0;
   }
}
代码中提及的EMPTY_INTS及EMPTY_OBJECTS,仅仅如下的两个空数组:
static final int[] EMPTY_INTS = new int[0];

static final Object[] EMPTY_OBJECTS = new Object[0];
在Hash数组中,采取二分查找,定位到指定hash值所对应的index值;之后根据index值,来调整并存放key跟value的值

public V get(Object key) {
   final int index = indexOfKey(key);
   return index >= 0 ? (V)mArray[(index<<1)+1] : null;
}
通过key来获取数据就非常简单了,根据key获取到相应的index值,在array数据中根据index乘2加1返回相应的value即可。
public int indexOfKey(Object key) {
   return key == null ? indexOfNull() : indexOf(key, key.hashCode());
}

由上发现允许key为null,进行index的查询,当key不为空时,通过key及其key的hashCode,来进行查询。
代码中,是先对Hash数组进行二分查找,获取index,之后根据index获取hash数组中对应的值,通过与key来比较是否相等,相等则直接返回,若不相等,则先从index之后的数据进行比较,没找到,则再找之前的数据。可以看出这样是支持存在多个key的hash值相同的情况,那再看看支不支持多个key为null的情况呢?
int indexOf(Object key, int hash) {
    final int N = mSize;

    // Important fast case: if nothing is in here, nothing to look for.
    if (N == 0) {
        return ~0;
    }

    int index = ContainerHelpers.binarySearch(mHashes, N, hash);

    // If the hash code wasn't found, then we have no entry for this key.
    if (index < 0) {
        return index;
    }

    // If the key at the returned index matches, that's what we want.
    if (key.equals(mArray[index<<1])) {
        return index;
    }

    // Search for a matching key after the index.
    int end;
    for (end = index + 1; end < N && mHashes[end] == hash; end++) {
        if (key.equals(mArray[end << 1])) return end;
    }

    // Search for a matching key before the index.
    for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
        if (key.equals(mArray[i << 1])) return i;
    }

    // Key not found -- return negative value indicating where a
    // new entry for this key should go.  We use the end of the
    // hash chain to reduce the number of array entries that will
    // need to be copied when inserting.
    return ~end;
}
从上可以看出当key为null的时候,采取获取的方法跟key不为null获取是很相似的了,都要进行整个数组的遍历,不过这里对应的hash都是为0。但key为null只能在数组中存在一个的,因为在数据的put操作的时候,会对key进行检查,这样保证了key为null只能存在一个。
int indexOfNull() {
    final int N = mSize;

    // Important fast case: if nothing is in here, nothing to look for.
    if (N == 0) {
        return ~0;
    }

    int index = ContainerHelpers.binarySearch(mHashes, N, 0);

    // If the hash code wasn't found, then we have no entry for this key.
    if (index < 0) {
        return index;
    }

    // If the key at the returned index matches, that's what we want.
    if (null == mArray[index<<1]) {
        return index;
    }

    // Search for a matching key after the index.
    int end;
    for (end = index + 1; end < N && mHashes[end] == 0; end++) {
        if (null == mArray[end << 1]) return end;
    }

    // Search for a matching key before the index.
    for (int i = index - 1; i >= 0 && mHashes[i] == 0; i--) {
        if (null == mArray[i << 1]) return i;
    }

    // Key not found -- return negative value indicating where a
    // new entry for this key should go.  We use the end of the
    // hash chain to reduce the number of array entries that will
    // need to be copied when inserting.
    return ~end;
}

public V remove(Object key) {
   final int index = indexOfKey(key);
   if (index >= 0) {
      return removeAt(index);
   }

   return null;
}
这里先忽略hash数组长度的判断(主要进行数组缓存的操作)只看主要的代码,即最后的一个else的代码,使用System.arraycopy方法将hash数组跟array数组中index之后的数据往前移动1位,而将最后一位的数据进行至空。
public V removeAt(int index) {
   final Object old = mArray[(index << 1) + 1];
   if (mSize <= 1) {
      // Now empty.
      if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
      freeArrays(mHashes, mArray, mSize);
      mHashes = ContainerHelpers.EMPTY_INTS;
      mArray = ContainerHelpers.EMPTY_OBJECTS;
      mSize = 0;
   } else {
      // 满足条件,对数组进行加入缓存的操作。
      if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {
         // Shrunk enough to reduce size of arrays.  We don't allow it to
         // shrink smaller than (BASE_SIZE*2) to avoid flapping between
         // that and BASE_SIZE.
         final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2);

         if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);

         final int[] ohashes = mHashes;
         final Object[] oarray = mArray;
         allocArrays(n);

         mSize--;
         if (index > 0) {
            if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");
            System.arraycopy(ohashes, 0, mHashes, 0, index);
            System.arraycopy(oarray, 0, mArray, 0, index << 1);
         }
         if (index < mSize) {
            if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + mSize
                  + " to " + index);
            System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);
            System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1,
                  (mSize - index) << 1);
         }
      } else {
         mSize--;
         if (index < mSize) {
            if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + mSize
                  + " to " + index);
            System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);
            System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1,
                  (mSize - index) << 1);
         }
         mArray[mSize << 1] = null;
         mArray[(mSize << 1) + 1] = null;
      }
   }
   return (V)old;
}
public V get(Object key) {
   final int index = indexOfKey(key);
   return index >= 0 ? (V)mArray[(index<<1)+1] : null;
}
代码中,可以看出arrayMap允许key为空,所有的key都不能重复。
另外,在进行容量修改的时候,进行的操作是:mSize跟hash数组长度的判断,当大于等于的时候,需要对数组的容量进行一些扩容,并拷贝数组到新的数组中。(扩容操作:当size大于8, 取size + size /2 ; 当size大于4小于8时, 取8 ,当size小于4时,取4)
public V put(K key, V value) {
   final int hash;
   int index;
   if (key == null) {
      // 查找key为null的情况
      hash = 0;
      index = indexOfNull();
   } else {
      hash = key.hashCode();
      index = indexOf(key, hash);
   }
   if (index >= 0) {
      // 数组中存在相同的key,则更新并返回旧的值
      index = (index<<1) + 1;
      final V old = (V)mArray[index];
      mArray[index] = value;
      return old;
   }

   index = ~index;
   if (mSize >= mHashes.length) {
      // 当容量不够时,需要建立一个新的数组,来进行扩容操作。
      final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))
         : (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);

      if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);

      final int[] ohashes = mHashes;
      final Object[] oarray = mArray;
      allocArrays(n);

      if (mHashes.length > 0) {
         if (DEBUG) Log.d(TAG, "put: copy 0-" + mSize + " to 0");
         System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
         System.arraycopy(oarray, 0, mArray, 0, oarray.length);
      }

      freeArrays(ohashes, oarray, mSize);
   }

   // 将index之后的数据进行后移
   if (index < mSize) {
      if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (mSize-index)
            + " to " + (index+1));
      System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
      System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
   }

   // 赋值给index位置上hash值
   mHashes[index] = hash;
   // 更新array数组中对应的key跟value值。
   mArray[index<<1] = key;
   mArray[(index<<1)+1] = value;
   mSize++;
   return null;
}
讲到这里,就基本可以结束了,而源码中看到了两个神奇的数组,他俩主要的目的是对固定的数组来进行缓存,官方给的说法是避免内存抖动,毕竟这里是纯数组来实现的,而当数组容量不够的时候,就需要建立一个新的数组,这样旧的数组不就浪费了,所以这里的缓存还是灰常必要的。接下来看看他俩是怎样玩的
private static final int BASE_SIZE = 4;

/**
 * Maximum number of entries to have in array caches.
 */
private static final int CACHE_SIZE = 10;

/**
 * Caches of small array objects to avoid spamming garbage.  The cache
 * Object[] variable is a pointer to a linked list of array objects.
 * The first entry in the array is a pointer to the next array in the
 * list; the second entry is a pointer to the int[] hash code array for it.
 */
static Object[] mBaseCache;
static int mBaseCacheSize;
static Object[] mTwiceBaseCache;
static int mTwiceBaseCacheSize;
代码中有两个静态的Object数组,这两个静态数组采用链表的方式来缓存所有的数组。即Object数组会用来指向array数组,而这个array的第一个值为指针,指向下一个array,而第二个值是对应的hash数组,其他的值则为空。另外,缓存数组即baseCache和twiceBaseCache,它俩大小容量的限制:最小值为4,最大值为10,而BaseCache数组主要存储的是容量为4的数组,twiceBaseCache主要存储容量为8的数组。
这个时候,当size跟缓存的数组大小相同,即要么等于4,要么等于8,即可从缓存中拿取数组来用。这里主要的操作就是baseCache指针的移动,指向array[0]指向的指针,hash数组即为array[0],而当前的这个array咱们就可以使用了。
  • SimpleArrayMap是可以替代ArrayMap来使用的,区别只是其内部采用单纯的数组来实现,而ArrayMap中采用了EntrySet跟KeySet的结构,这样方便使用Iterator来数据的遍历获取。
  • ArrayMap适用于少量的数据,因为存取的复杂度,对数量过大的就不太合适。这个量笔者建议破百就放弃ArrayMap的使用吧。
  • ArrayMap支持key为null,但数组只能有一个key为null的存在。另外,允许多个key的hash值相同,不过尽量避免吧,不然二分查找获取不到,又会进行遍历查找;而key都必须是唯一,不能重复的。
  • 主要目的是避免占用大量的内存切无法得到地充分利用。
  • 对容量为4和容量为8的数组,进行缓存,来防止内存抖动的发生。


Sunday, May 8, 2016

Implement your own tail (Read last n lines of a huge file) - GeeksforGeeks



Implement your own tail (Read last n lines of a huge file) - GeeksforGeeks
Given a huge file having dynamic data, write a program to read last n lines from the file at any point without reading the entire file. The problem is similar to tail command in linux which displays the last few lines of a file. It is mostly used for viewing log file updates as these updates are appended to the log files.

The problem mainly focuses on below things –
1. The program should not read entire file.
2. The program should handle incoming dynamic data and returns last n lines at any point.
3. The program should not close input stream before reading last n lines.
void tail(FILE* in, int n)
{
    int count = 0;  // To count '\n' characters
    // unsigned long long pos (stores upto 2^64 – 1
    // chars) assuming that long long int takes 8
    // bytes
    unsigned long long pos;
    char str[2*SIZE];
    // Go to End of file
    if (fseek(in, 0, SEEK_END))
        perror("fseek() failed");
    else
    {
        // pos will contain no. of chars in
        // input file.
        pos = ftell(in);
        // search for '\n' characters
        while (pos)
        {
            // Move 'pos' away from end of file.
            if (!fseek(in, --pos, SEEK_SET))
            {
                if (fgetc(in) == '\n')
                    // stop reading when n newlines
                    // is found
                    if (count++ == n)
                        break;
            }
            else
                perror("fseek() failed");
        }
        // print last n lines
        printf("Printing last %d lines -\n", n);
        while (fgets(str, sizeof(str), in))
            printf("%s", str);
    }
    printf("\n\n");
}
http://netjs.blogspot.in/2016/02/how-to-read-file-from-last-line-in-java.html
 public void readFromLast(File file, int lines){
  int readLines = 0;
        StringBuilder builder = new StringBuilder();
        RandomAccessFile randomAccessFile = null;
        try {
         randomAccessFile = new RandomAccessFile(file, "r");
         long fileLength = file.length() - 1;
         // Set the pointer at the last of the file
         randomAccessFile.seek(fileLength);
         for(long pointer = fileLength; pointer >= 0; pointer--){
          randomAccessFile.seek(pointer);
          char c;
          // read from the last one char at the time
    c = (char)randomAccessFile.read(); 
    // break when end of the line
             if(c == '\n'){
              readLines++;
              if(readLines == lines)
               break;
             }
             builder.append(c);
         }
         // Since line is read from the last so it 
         // is in reverse so use reverse method to make it right
         builder.reverse();
         System.out.println("Line - " + builder.toString());
        } catch (FileNotFoundException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
        catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }finally{
  if(randomAccessFile != null){
   try {
    randomAccessFile.close();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   }
  }
   }

Using Apache Commons IO
public void reverseLines(File file){
 ReversedLinesFileReader object = null;
 try {
  object = new ReversedLinesFileReader(file);
  System.out.println("Line - " + object.readLine());
 } catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }finally{
  try {
   object.close();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}
Read full article from Implement your own tail (Read last n lines of a huge file) - GeeksforGeeks

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts