使用素数解决表示多个标签组合查询

某一个商品可能有多个标签属性,比如A,B,C,D,E,F,G,H

每个属性分别有出现与不出现的情况。。所有是一个组合的关系 。用一个二进制的字符串或者数字来表示出现与不出现,但是这样要搜索包含某个标签 的时候,得枚举所有的情况,然后以OR的关系去搜索。这样肯定麻烦。。既然要为了存储与索引搜索方便,我暂时想用素数来解决。。


对每一个标签分配一个素数,比如A-》2,B->3,C->5,D->7


这样多个标签的组合就使用他们的乘积作为最终状态。。这样是一种编码的广式,

比如包含A,B,C

则对应的值是2*3*5=30


当然也需要解码,对于给定的一个值30

如果能整除对应的素数集合,则表示包含该标签


30的素数因子为:2,3,5  为相应的标签。简单的实现代码如下。。




import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @author genuine 房源标签 <br>
 *         使用素数方式存储
 * 
 */
public enum Label {

	/**
	 * 新推
	 */
	N(2),

	/**
	 * 随时看房
	 */
	K(3),

	/**
	 * 视频
	 */
	V(5);

	/**
	 * 素数
	 */
	private final int primeV;

	private Label(int v) {
		this.primeV = v;
	}

	public int getPrimeV() {
		return primeV;
	}

	/**
	 * 
	 * 通过一个数字找到对应的所有特征
	 * 
	 * @param v
	 * @return
	 */
	public static Label[] decodedLabel(int v) {
		List<Label> list = new ArrayList<Label>();
		Label[] labels = Label.values();
		int temp = v;
		for (int i = 0; i < labels.length; i++) {
			if (temp % labels[i].getPrimeV() == 0) {
				list.add(labels[i]);
				temp = temp / labels[i].getPrimeV();
			}
		}
		if (temp != 1) {
			System.err.println("是非法值");
			return null;
		}
		return list.toArray(new Label[list.size()]);
	}

	
	private final static Map<Integer,int[]> cacheIncludeByLabel=new HashMap<Integer,int[]>();
	
	/**
	 * 查询包含某几个标签的时候的所有可能的组合值
	 * 
	 * @param labels
	 * @return
	 */
	public static int[] includeByLabel(Label... labels) {
		if (labels.length < 1) {
			return null;
		}
		//查找缓存,这样可以减少运算
		Integer key=encodedLabels(labels);
		int[] cache = cacheIncludeByLabel.get(key);
		if(cache!=null){
			return cache;
		}
		Label[] allLabels = Label.values();
		// 获得其它标签
		List<Label> otherList = new ArrayList<Label>(allLabels.length);
		for (int i = 0; i < allLabels.length; i++) {
			boolean isExist = false;
			for (int j = 0; j < labels.length; j++) {
				if (allLabels[i].getPrimeV() == labels[j].getPrimeV()) {
					isExist = true;
				}
			}
			if (!isExist) {
				otherList.add(allLabels[i]);
			}
		}
		int sum = 1;
		for (int i = 0; i < labels.length; i++) {
			sum *= labels[i].getPrimeV();
		}
		final int otherSize = otherList.size();
		final int size = otherSize * 2;
		final int[] includeV = new int[size];
		int pos = 0;
		includeV[pos++] = sum;// 纯包含指定的label

		// 计算不同的组合的值
		for (int i = pos; i < size; i++) {
			int[] compose = new int[otherSize];
			toBinary(i, compose);
			int tempSum = sum;
			for (int j = 0; j < otherSize; j++) {
				if (compose[j] == 1) {
					tempSum *= otherList.get(j).getPrimeV();
				}
			}
			includeV[i] = tempSum;
		}
		//缓存
		cacheIncludeByLabel.put(key, includeV);
		return includeV;
	}

	/**
	 * 通过多个标签返回最终数字
	 * 如果参数非法则会返回值1
	 * @param labels
	 * @return
	 */
	public static int encodedLabels(Label[] labels) {
		if (labels == null) {
			return 1;
		}
		int sum = 1;
		for (int i = 0; i < labels.length; i++) {
			sum *= labels[i].getPrimeV();
		}
		return sum;
	}

	public static int encodedLabel(Label label) {
		if (label == null) {
			return 1;
		}
		return label.getPrimeV();
	}

	/**
	 * 通过多个标签返回最终数字
	 * 如果参数非法则会返回值1
	 * @param labels
	 * @return
	 */
	public static int encodedLabels(Iterator<Label> labels) {
		if (labels == null) {
			return 1;
		}
		int sum = 1;
		while (labels.hasNext()) {
			sum *= labels.next().getPrimeV();
		}
		return sum;
	}

	/**
	 * 给定数字,返回该数字的二进制表示并存在一个int数组中
	 */
	private static void toBinary(int inputNum, int[] result) {
		int pos = result.length;
		do {
			result[--pos] = inputNum & 1;
			inputNum >>>= 1;
		} while (inputNum != 0);
	}

	public static void main(String[] args) {

		// int i = 15;
		// Label[] label = Label.getLabel(i);
		//
		// for (int j = 0; j < label.length; j++) {
		// System.out.println(label[j]);
		// }
		//
		// System.out.println("------------------------------");

		Label k = Label.K;
		Label n = Label.N;

		int a[] = Label.includeByLabel(n, k);

		System.out.println(Arrays.toString(a));
	}

}

















这样查询多个状态组合的时候,


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值