Java实现基于数值编码原理的组合算法(单线程)

http://hi.baidu.com/shirdrn/blog/item/58118bf70681892b730eec6b.html

组合算法思想:

本程序的思路是开一个数组,其下标表示1到m个数,数组元素的值为1表示其下标 代表的数被选中,为0则没选中。    
首先初始化,将数组前n个元素置1,表示第一个组合为前n个数。然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为 “01”组合,同时将其左边的所有“1”全部移动到数组的最左端。 当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得到了最后一个组合。

该思想是基于数值编码原理的,在Java中可以使用BitSet来代替一个数组,用来表示某个字符串的某些位是否被占用,如果被占用,则为true,并根据上述思想构造的BitSet来确定某一个组合。

例如,对字符串12345进行5取2组合:

11100 -> ***45

11010 -> **3*5

10110 -> *2**5

01110 -> 1***5

11001 -> **34*

10101 -> *2*4*

01101 -> 1**4*

10011 -> *23**

10100 -> 1*3**

00111 -> 12***

通过分析可知,如果需要将遇到10后左侧的1进行调整,那么此时0一定位于最左侧,也就是说,最左侧可能存在1个0或者连续个0。

根据上述分析,使用单线程,对每一个字符串创建一个线程进行组合处理,用Java实现组合过程:

首先创建一个能够对单独字符串进行组合的单线程SplitterThread类,在该线程类中实现单个字符串的组合逻辑实现;

然后将其作为Splitter的内部类,在Splitter中提供每个线程类所需要的公共变量,例如星号字符的个数,字符串的集合(需要对各个线程进行同步)。

最后等待所有的线程都执行完成,合并组合的结果集。

代码如下所示:

package org.shirdrn;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

/**
* 组合拆分类
*
* 根据从复式投注字符串经过拆分过滤后得到的单式投注字符串集合,对其中每一个单式投注字符串进行组合<br>
* 例如:输入集合{31311133,33113330},Splitter类会遍历该集合,对每个单式投注注字符串,创建一个SplitterThread<br>
* 线程来处理,如果是2串1组合,即starCount=8-2=6,经过线程处理得到类似******33,*****1*3等结果
*
* @author shirdrn
*
*/
public class Splitter {

private int starCount;
private boolean duplicate;
private Collection<String> filteredContainer;

public Collection<String> getFilteredContainer() {
   return filteredContainer;
}

/**
* 构造一个Spilitter实例
*
* @param container 从复式投注字符串经过拆分过滤后得到的单式投注字符串集合
* @param starCount 如果对于N长比赛,进行M组合过关,则starCount=N-M
* @param duplicate 是否去重
*/
public Splitter(Collection<String> container, int starCount, boolean duplicate) {
   this.duplicate = duplicate;
   this.starCount = starCount;
   if(this.duplicate) { // 根据指定是否去重的选择,选择创建容器
    filteredContainer = Collections.synchronizedSet(new HashSet<String>());
   }
   else {
    filteredContainer = Collections.synchronizedList(new ArrayList<String>());
   }
   Iterator<String> it = container.iterator();
   while(it.hasNext()) {
    new Thread(new SplitterThread(it.next().trim())).start();
   }
   try {
    Thread.sleep(50);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
}

/**
* 对一个指定的N场比赛的长度为N的单式投注字符串进行组合<br>
* 输入单式投注注字符串string,例如31311133,组合得到类似******33,*****1*3,... ...结果的集合
*
* @author shirdrn
*
*/
class SplitterThread implements Runnable {

   private char[] charArray;
   private int len;

   private List<String> container = new ArrayList<String>();
   private BitSet startBitSet; // 比特集合起始状态
   private BitSet endBitSet; // 比特集合终止状态,用来控制循环

   public SplitterThread(String string) {
    this.charArray = string.toCharArray();
    this.len = string.length();
    this.startBitSet = new BitSet(len);
    this.endBitSet = new BitSet(len);
  
    // 初始化startBitSet,左侧占满*符号
    for (int i = 0; i < starCount; i++) {
     this.startBitSet.set(i, true);
    }
    // 初始化endBit,右侧占满*符号
    for (int i = this.len - 1; i > this.len - starCount - 1; i--) {
     this.endBitSet.set(i, true);
    }
    // 根据起始startBitSet,构造带*的组合字符串并加入容器
    char[] charArrayClone = this.charArray.clone();
    for (int i = 0; i < len - 1; i++) {
     if (this.startBitSet.get(i)) {
      charArrayClone[i] = '*';
     }
    }
    this.container.add(new String(charArrayClone));
   }

   public void run() {
    this.split();
    synchronized(filteredContainer) {
     filteredContainer.addAll(this.container);
    }
   }

   public void split() {
    while (!this.startBitSet.equals(this.endBitSet)) {
     int zeroCount = 0; // 统计遇到10后,左边0的个数
     int oneCount = 0; // 统计遇到10后,左边1的个数
     int pos = 0; // 记录当前遇到10的索引位置
     char[] charArrayClone = this.charArray.clone();
   
     // 遍历startBitSet来确定10出现的位置
     for (int i = 0; i < len - 1; i++) {
      if (!this.startBitSet.get(i)) {
       zeroCount++;
      }
      if (this.startBitSet.get(i) && !this.startBitSet.get(i + 1)) {
       pos = i;
       oneCount = i - zeroCount;
       this.startBitSet.set(i, false);
       this.startBitSet.set(i + 1, true);
       break;
      }
     }
     // 将遇到10后,左侧的1全部移动到最左侧
     int count = Math.min(zeroCount, oneCount);
     int startIndex = 0;
     int endIndex = pos - 1;
     for (int i = 0; i < count; i++) {
      this.startBitSet.set(startIndex, true);
      this.startBitSet.set(endIndex, false);
      startIndex++;
      endIndex--;
     }
     // 将遇到1的位置用*替换
     for (int i = 0; i < len; i++) {
      if (this.startBitSet.get(i)) {
       charArrayClone[i] = '*';
      }
     }
     this.container.add(new String(charArrayClone));
    }
   }
}
}

测试用例如下所示:

package org.shirdrn;

import java.util.ArrayList;
import java.util.Collection;

import junit.framework.TestCase;

public class TestSplitter extends TestCase {

private Splitter splitter;

public void setSplitter(Collection<String> container, int starCount, boolean duplicate) {
   this.splitter = new Splitter(container, starCount, duplicate);
}

// 5取3组合
public void testSplitter1() {
   Collection<String> container = new ArrayList<String>();
   container.add("12345");
   int starCount = 3;
   boolean duplicate = false;
   this.setSplitter(container, starCount, duplicate);
   System.out.println(this.splitter.getFilteredContainer());
   assertEquals(10, this.splitter.getFilteredContainer().size());
}

// 9取4组合
public void testSplitter2() {
   Collection<String> container = new ArrayList<String>();
   container.add("333301110");
   container.add("310131300");
   container.add("301100111");
   int starCount = 4;
   boolean duplicate = false;
   this.setSplitter(container, starCount, duplicate);
   System.out.println(this.splitter.getFilteredContainer());
   assertEquals(126*3, this.splitter.getFilteredContainer().size());
}
}

结果如下所示:

[***45, **3*5, *2**5, 1***5, **34*, *2*4*, 1**4*, *23**, 1*3**, 12***]
[****00111, ***1*0111, **1**0111, *0***0111, 3****0111, ***10*111, **1*0*111, *0**0*111, 3***0*111, **11**111, *0*1**111, 3**1**111, *01***111, 3*1***111, 30****111, ***100*11, **1*00*11, *0**00*11, 3***00*11, **11*0*11, *0*1*0*11, 3**1*0*11, *01**0*11, 3*1**0*11, 30***0*11, **110**11, *0*10**11, 3**10**11, *01*0**11, 3*1*0**11, 30**0**11, *011***11, 3*11***11, 30*1***11, 301****11, ***1001*1, **1*001*1, *0**001*1, 3***001*1, **11*01*1, *0*1*01*1, 3**1*01*1, *01**01*1, 3*1**01*1, 30***01*1, **110*1*1, *0*10*1*1, 3**10*1*1, *01*0*1*1, 3*1*0*1*1, 30**0*1*1, *011**1*1, 3*11**1*1, 30*1**1*1, 301***1*1, **1100**1, *0*100**1, 3**100**1, *01*00**1, 3*1*00**1, 30**00**1, *011*0**1, 3*11*0**1, 30*1*0**1, 301**0**1, *0110***1, 3*110***1, 30*10***1, 301*0***1, 3011****1, ***10011*, **1*0011*, *0**0011*, 3***0011*, **11*011*, *0*1*011*, 3**1*011*, *01**011*, 3*1**011*, 30***011*, **110*11*, *0*10*11*, 3**10*11*, *01*0*11*, 3*1*0*11*, 30**0*11*, *011**11*, 3*11**11*, 30*1**11*, 301***11*, **1100*1*, *0*100*1*, 3**100*1*, *01*00*1*, 3*1*00*1*, 30**00*1*, *011*0*1*, 3*11*0*1*, 30*1*0*1*, 301**0*1*, *0110**1*, 3*110**1*, 30*10**1*, 301*0**1*, 3011***1*, **11001**, *0*1001**, 3**1001**, *01*001**, 3*1*001**, 30**001**, *011*01**, 3*11*01**, 30*1*01**, 301**01**, *0110*1**, 3*110*1**, 30*10*1**, 301*0*1**, 3011**1**, *01100***, 3*1100***, 30*100***, 301*00***, 3011*0***, 30110****, ****01110, ***3*1110, **3**1110, *3***1110, 3****1110, ***30*110, **3*0*110, *3**0*110, 3***0*110, **33**110, *3*3**110, 3**3**110, *33***110, 3*3***110, 33****110, ***301*10, **3*01*10, *3**01*10, 3***01*10, **33*1*10, *3*3*1*10, 3**3*1*10, *33**1*10, 3*3**1*10, 33***1*10, **330**10, *3*30**10, 3**30**10, *33*0**10, 3*3*0**10, 33**0**10, *333***10, 3*33***10, 33*3***10, 333****10, ***3011*0, **3*011*0, *3**011*0, 3***011*0, **33*11*0, *3*3*11*0, 3**3*11*0, *33**11*0, 3*3**11*0, 33***11*0, **330*1*0, *3*30*1*0, 3**30*1*0, *33*0*1*0, 3*3*0*1*0, 33**0*1*0, *333**1*0, 3*33**1*0, 33*3**1*0, 333***1*0, **3301**0, *3*301**0, 3**301**0, *33*01**0, 3*3*01**0, 33**01**0, *333*1**0, 3*33*1**0, 33*3*1**0, 333**1**0, *3330***0, 3*330***0, 33*30***0, 333*0***0, 3333****0, ***30111*, **3*0111*, *3**0111*, 3***0111*, **33*111*, *3*3*111*, 3**3*111*, *33**111*, 3*3**111*, 33***111*, **330*11*, *3*30*11*, 3**30*11*, *33*0*11*, 3*3*0*11*, 33**0*11*, *333**11*, 3*33**11*, 33*3**11*, 333***11*, **3301*1*, *3*301*1*, 3**301*1*, *33*01*1*, 3*3*01*1*, 33**01*1*, *333*1*1*, 3*33*1*1*, 33*3*1*1*, 333**1*1*, *3330**1*, 3*330**1*, 33*30**1*, 333*0**1*, 3333***1*, **33011**, *3*3011**, 3**3011**, *33*011**, 3*3*011**, 33**011**, *333*11**, 3*33*11**, 33*3*11**, 333**11**, *3330*1**, 3*330*1**, 33*30*1**, 333*0*1**, 3333**1**, *33301***, 3*3301***, 33*301***, 333*01***, 3333*1***, 33330****, ****31300, ***1*1300, **0**1300, *1***1300, 3****1300, ***13*300, **0*3*300, *1**3*300, 3***3*300, **01**300, *1*1**300, 3**1**300, *10***300, 3*0***300, 31****300, ***131*00, **0*31*00, *1**31*00, 3***31*00, **01*1*00, *1*1*1*00, 3**1*1*00, *10**1*00, 3*0**1*00, 31***1*00, **013**00, *1*13**00, 3**13**00, *10*3**00, 3*0*3**00, 31**3**00, *101***00, 3*01***00, 31*1***00, 310****00, ***1313*0, **0*313*0, *1**313*0, 3***313*0, **01*13*0, *1*1*13*0, 3**1*13*0, *10**13*0, 3*0**13*0, 31***13*0, **013*3*0, *1*13*3*0, 3**13*3*0, *10*3*3*0, 3*0*3*3*0, 31**3*3*0, *101**3*0, 3*01**3*0, 31*1**3*0, 310***3*0, **0131**0, *1*131**0, 3**131**0, *10*31**0, 3*0*31**0, 31**31**0, *101*1**0, 3*01*1**0, 31*1*1**0, 310**1**0, *1013***0, 3*013***0, 31*13***0, 310*3***0, 3101****0, ***13130*, **0*3130*, *1**3130*, 3***3130*, **01*130*, *1*1*130*, 3**1*130*, *10**130*, 3*0**130*, 31***130*, **013*30*, *1*13*30*, 3**13*30*, *10*3*30*, 3*0*3*30*, 31**3*30*, *101**30*, 3*01**30*, 31*1**30*, 310***30*, **0131*0*, *1*131*0*, 3**131*0*, *10*31*0*, 3*0*31*0*, 31**31*0*, *101*1*0*, 3*01*1*0*, 31*1*1*0*, 310**1*0*, *1013**0*, 3*013**0*, 31*13**0*, 310*3**0*, 3101***0*, **01313**, *1*1313**, 3**1313**, *10*313**, 3*0*313**, 31**313**, *101*13**, 3*01*13**, 31*1*13**, 310**13**, *1013*3**, 3*013*3**, 31*13*3**, 310*3*3**, 3101**3**, *10131***, 3*0131***, 31*131***, 310*31***, 3101*1***, 31013****]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值