基于数值编码原理的组合算法思想:
本程序的思路是开一个数组,其下标表示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
11001 -> **34*
10101 -> *2*4*
01101 -> 1**4*
10011 -> *23**
10100 -> 1*3**
00111 -> 12***
使用Java实现基于多线程的组合算法,更多的地方需要同步访问,在一定程度上能够提高并行度,提高计算速度。实现代码如下所示:
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;/**
* 组合拆分类
*
* 根据从复式投注字符串经过拆分过滤后得到的单式投注字符串集合,对其中每一个单式投注字符串进行组合<br>
* 例如:输入集合{31311133,33113330},Splitter类会遍历该集合,对每个单式投注注字符串,创建一个SplitterThread<br>
* 线程来处理,如果是2串1组合,即starCount=8-2=6,经过线程处理得到类似******33,*****1*3等结果
*
* @author shirdrn
*
*/
public class MySplitter {
private Iterator<String> iterator;
private int length;
private int starCount;
private boolean duplicate;
private Collection<String> filteredContainer;
private Boolean finished = false;
private Integer threadCount;
private int splitSize = 100;
public Collection<String> getFilteredContainer() {
while(!finished) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return filteredContainer;
}
/**
* 构造一个Spilitter实例
*
* @param container 从复式投注字符串经过拆分过滤后得到的单式投注字符串集合
* @param starCount 如果对于N长比赛,进行M组合过关,则starCount=N-M
* @param duplicate 是否去重
*/
public MySplitter(Collection<String> container, int starCount, boolean duplicate) {
this.iterator = container.iterator();
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();
this.length = it.next().trim().length();
this.threadCount = container.size()/splitSize;
for(int i=0; i<this.threadCount; i++) {
new Thread(new SplitterThread()).start();
}
}
/**
* 对一个指定的N场比赛的长度为N的单式投注字符串进行组合<br>
* 输入单式投注注字符串string,例如31311133,组合得到类似******33,*****1*3,... ...结果的集合
*
* @author shirdrn
*
*/
class SplitterThread implements Runnable {
public SplitterThread() {
}
public void run() {
boolean flag = true;
while(flag) {
Collection<String> c;
if(duplicate) { // 根据指定是否去重的选择,选择创建线程的私有容器
c = new HashSet<String>();
}
else {
c = new ArrayList<String>();
}
String string = "";
synchronized(iterator) { // 需要同步待处理字符串集合的迭代器实例,迭代一条记录进行处理
if(iterator.hasNext()) {
string = iterator.next();
}
else {
synchronized(threadCount) { // 同步线程统计变量,用于监测当前线程总数,即使Kill掉完成任务的线程
threadCount--;
if(threadCount == 0) {
finished = true;
flag = false;
}
break;
}
}
}
this.split(string, c);
synchronized(filteredContainer) { // 同步处理后的集合,将处理结果整合到容器中
filteredContainer.addAll(c);
}
}
}
public void split(String string, Collection<String> c) {
char[] charArray = string.toCharArray();
BitSet startBitSet = new BitSet(length); // 比特集合起始状态
BitSet endBitSet = new BitSet(length); // 比特集合终止状态,用来控制循环
// 初始化startBitSet,左侧占满*符号
for (int i = 0; i < starCount; i++) {
startBitSet.set(i, true);
}
// 初始化endBit,右侧占满*符号
for (int i = length - 1; i > length - starCount - 1; i--) {
endBitSet.set(i, true);
}
// 根据起始startBitSet,构造带*的组合字符串并加入容器
char[] charArrayClone = charArray.clone();
for (int i = 0; i < length - 1; i++) {
if (startBitSet.get(i)) {
charArrayClone[i] = '*';
}
}
c.add(new String(charArrayClone));
while (!startBitSet.equals(endBitSet)) {
int zeroCount = 0; // 统计遇到10后,左边0的个数
int oneCount = 0; // 统计遇到10后,左边1的个数
int pos = 0; // 记录当前遇到10的索引位置
charArrayClone = charArray.clone();
// 遍历startBitSet来确定10出现的位置
for (int i = 0; i < length - 1; i++) {
if (!startBitSet.get(i)) {
zeroCount++;
}
if (startBitSet.get(i) && !startBitSet.get(i + 1)) {
pos = i;
oneCount = i - zeroCount;
startBitSet.set(i, false);
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++) {
startBitSet.set(startIndex, true);
startBitSet.set(endIndex, false);
startIndex++;
endIndex--;
}
// 将遇到1的位置用*替换
for (int i = 0; i < length; i++) {
if (startBitSet.get(i)) {
charArrayClone[i] = '*';
}
}
c.add(new String(charArrayClone));
}
}
}
}测试用例如下所示:
package org.shirdrn;
import java.util.Collection;
import com.zucai310.Zucai310;
import junit.framework.TestCase;
public class TestMySplitter extends TestCase {
private MySplitter splitter;
public void setMySplitter(Collection<String> container, int starCount, boolean duplicate) {
this.splitter = new MySplitter(container, starCount, duplicate);
}
public void testMySplitter_8_310() {
// 8 场:310
String multiSeq = "310,310,310,310,310,310,310,310";
Collection<String> container = Zucai310.getNSingleList(multiSeq);
assertEquals(6561, container.size());
int starCount = 4;
boolean duplicate = false;
this.setMySplitter(container, starCount, duplicate);
assertEquals(459270, this.splitter.getFilteredContainer().size());
}
public void testMySplitter_9_1() {
// 9 场:310,310,310,310,310,310,310,310,3
String multiSeq = "310,310,310,310,310,310,310,310,3";
Collection<String> container = Zucai310.getNSingleList(multiSeq);
assertEquals(6561, container.size());
int starCount = 4;
boolean duplicate = false;
this.setMySplitter(container, starCount, duplicate);
assertEquals(826686, this.splitter.getFilteredContainer().size());
}
public void testMySplitter_9_2() {
// 9 场:310,310,310,310,310,310,310,310,3
String multiSeq = "310,310,310,310,310,310,31,31,31";
Collection<String> container = Zucai310.getNSingleList(multiSeq);
assertEquals(5832, container.size());
int starCount = 4;
boolean duplicate = false;
this.setMySplitter(container, starCount, duplicate);
assertEquals(734832, this.splitter.getFilteredContainer().size());
}
}