频繁数据集挖掘的一种实现方式
来源:互联网 发布:下载天际通软件 编辑:程序博客网 时间:2024/06/01 08:15
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.text.html.HTMLDocument.Iterator;
import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;
/**
* 本程序用于频繁集的挖掘 首先用List<List<String>>类型的record将矩阵形式的数据读入内存;
*
* 程序先求出k-1备选集,由备选集和数据库记录record求得满足支持度的k-1级集合,在满足支持度集合中求出满足自信度的集合,
* 若满足置信度的集合为空,程序停止; 否则输出满足自信度的集合,以及对应的支持度和自信度,并由满足支持度的k-1级集合求出k级备选集,进入下一轮循环;
* 直至程序结束,输出全部频繁级
*/
public class CopyOfApriorilesNew {
public static SimpleDateFormat DATE_TEMPLATE = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
public static List<List<String>> TIME_LABEL_LIST = new ArrayList<List<String>>();// 生成事务的日志事件
public static List<List<String>> transactions = new ArrayList<List<String>>(); // 事务序列
public static int windowSize = 30; // 窗口大小
public static int stepSize = 10; // 移动步长
//public static List<List<List<String>>> allITtemSets = new ArrayList<List<List<String>>>(); // 存放所有的候选项集(不包含候选1项集)
//public static List<List<List<String>>> allFItemSets = new ArrayList<List<List<String>>>(); // 存放所有的频繁项集(不包含频繁1项集)
//public static List<List<Integer>> allCandidateSupporCount = new ArrayList<List<Integer>>(); // 存放所有候选项集的事件数
public static List<List<Integer>> supporCount = new ArrayList<List<Integer>>(); // 存放每个频繁项集的事件数
public static List<List<List<String>>> eventRules = new ArrayList<List<List<String>>>(); // 存放所有的事件规则
static boolean endTag = false; // 循环是否进行的标志
final static int MIN_SUPPORT = 10; // 支持度阈值(事件数)
final static double MIN_CONF = -1; // 置信度阈值
//public static List<List<String>> allFItemSetConfidence = new ArrayList<List<String>>(); // 存放所有频繁项的置信度
public static List<List<String>> satisfiedConfidence = new ArrayList<List<String>>(); // 存放表示满足置信度阈值的(事件规则)频繁项的置信度
public static String exportConfidence = new String();
//public static int eventCount = 0;
/**
* @param args
* @throws ParseException
*/
public static void main(String[] args) throws ParseException {
// TODO Auto-generated method stub
getRecord(); // 获取按时间排序的日志记录
getTransactions(windowSize, stepSize); // 生成事务
List<String> oneFItemset = findFirstFrequenItemset(); // 获取一项频繁项集,用哈希表存储
List<List<String>> cItemset = findSencondCandidate(oneFItemset); // 获取二项候选项集
List<List<String>> fItemset = getSupportedItemset(cItemset); // 获取二项候选项集cItemset满足支持的集合
getSecondConfidencedItemset(fItemset, oneFItemset); // 获取二项事件规则
while (endTag != true) { // 只要能继续挖掘
List<List<String>> ckItemset = getNextCandidate(fItemset); // 获取第下一次的候选集
List<List<String>> fkItemset = getSupportedItemset(ckItemset); // 获取候选集cItemset满足支持的集合
getConfidencedItemset(fkItemset, fItemset); // 获取频繁项集满足置信度的集合
fItemset=fkItemset;
}
print();
System.out.print("The program is finished!");
}
// private static void test(){
// //String str="1a 2a 3a 4a 5a 1a 1a 1a 2a 3a 4a 1a 1a 5a 1a 1a 1a 2a 4a 5a 1a 1a 1a 1a 1a 1a";
// //String str="1a 2a 3a 4a";
// String str="1a 2a";
// String [] a=str.split(" ");
// List<String> se=new ArrayList<String>();
// se=Arrays.asList(a);
// List<String> Item=new ArrayList<String>();
// Item.add("1a");
// Item.add("2a");
// Item.add("3a");
// Item.add("4a");
// Item.add("5a");
// int in=countSupportCount(se,Item);
// System.out.print(in);
//
//
//
//
//
// }
/**
* 读取按时间排序后的日志
*/
private static void getRecord() {
String TimeSortedLogFile ="/home/wdl/test/alertLog_Sorted.txt";
try {
File File = new File(TimeSortedLogFile);
BufferedReader wReader = new BufferedReader(new InputStreamReader(
new FileInputStream(TimeSortedLogFile), "UTF-8"));
String line = null;
while ((line = wReader.readLine()) != null) {
if ("".equals(line.trim())) { // 遇空行,略过继续执行
continue;
}
List<String> tempList = new ArrayList<String>();
String[] logArr = line.split("\t");
tempList.add(logArr[0]);
tempList.add(logArr[1]);
TIME_LABEL_LIST.add(tempList);
}
wReader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
/**
* 获取事务
* @param windowSize 窗口大小
* @param stepSize
* @throws ParseException
*/
public static void getTransactions(int windowSize, int stepSize)
throws ParseException {
System.out.print("进入getTransaction函数");
List<List<String>> tempTransactions = new ArrayList<List<String>>();
// List<List<String>> transactions = new ArrayList<List<String>>();//
// 用来存放事务
int logMergeListLength = TIME_LABEL_LIST.size(); // 数据集大小
// 获取合并后的数据集的最早时间
Date minDate = DATE_TEMPLATE.parse(TIME_LABEL_LIST.get(0).get(0)); // 0存放的是时间
// 获取合并后的数据集的最晚时间
Date endDate = DATE_TEMPLATE.parse(TIME_LABEL_LIST.get(
logMergeListLength - 1).get(0));
// 设置开始时间
Calendar cal = Calendar.getInstance();
cal.setTime(minDate);
// 设置时间窗口大小
cal.add(Calendar.MINUTE, windowSize);
Date maxDate = cal.getTime();
Date stepDate = cal.getTime();
int curIndex = 0;// 当前时间
int stepIndex = 0;// 移动一个步长后所在的时间
while (minDate.getTime() < endDate.getTime()) {
// 保存当前时间窗口内的类别数据到set集合中
List<String> curLogSet = new ArrayList<String>(); // 改成用List存储,存储重复元素
curIndex = stepIndex; // 日志索引号赋值为上一个步长出的索引号,每次增加一个步长
cal.setTime(minDate);
cal.add(Calendar.MINUTE, stepSize);
stepDate = cal.getTime();
for (int i = curIndex; i < TIME_LABEL_LIST.size(); i++) {
Date timeStamp = DATE_TEMPLATE.parse(TIME_LABEL_LIST.get(i)
.get(0));
if (timeStamp.getTime() >= minDate.getTime()
&& timeStamp.getTime() < maxDate.getTime()) {
curLogSet.add(TIME_LABEL_LIST.get(i).get(1));
} else if (timeStamp.getTime() >= maxDate.getTime()) {
break;
}
if (timeStamp.getTime() <= stepDate.getTime()) {
stepIndex++; // 如果时间在当前步长内,日志索引号加一
}
}
// 窗口最大最小时间下滑一个步长
cal.setTime(minDate);
cal.add(Calendar.MINUTE, stepSize);
minDate = cal.getTime();
cal.setTime(maxDate);
cal.add(Calendar.MINUTE, stepSize);
maxDate = cal.getTime();
tempTransactions.add(curLogSet);
}
for (int i = 0; i < tempTransactions.size(); i++) { // 去除事务中为空的子序列
if (tempTransactions.get(i).size() != 0) {
transactions.add(tempTransactions.get(i));
}
}
}
/**
* 获取频繁一项集
* @return
*/
public static List<String> findFirstFrequenItemset() {
int count = 1;
Map<String, Integer> oneCandidateItemSetAndCount = new HashMap<String, Integer>();
Map<String, Integer> oneFrequentItemSetAndCount = new HashMap<String, Integer>();
for(List<String> transactionList:transactions ){ //从各个事务中得到频繁一项集
count = 1;
for(String tempStr:transactionList){
if(oneCandidateItemSetAndCount.containsKey(tempStr)){
count = (int) oneCandidateItemSetAndCount.get(tempStr) + 1;
}
oneCandidateItemSetAndCount.put( tempStr,count);
}
}
for (Entry<String, Integer> entry : oneCandidateItemSetAndCount // 测试,查看频繁一项集是否求解正确
.entrySet()) {
System.out.println("输出候选一项集:");
System.out.println(entry.getKey() + "=" + entry.getValue());
System.out.println("\r\n");
}
for (Entry<String, Integer> entry : oneCandidateItemSetAndCount // 求频繁一项集
.entrySet()) {
if ((int) entry.getValue() > MIN_SUPPORT) {
oneFrequentItemSetAndCount
.put(entry.getKey(), entry.getValue());
}
}
List<Integer> tempList = new ArrayList<Integer>();
for (Entry<String, Integer> entry : oneFrequentItemSetAndCount
.entrySet()) {
tempList.add(entry.getValue());
}
supporCount.add(tempList); // 将频繁一项集的事件数存储到supporCount中
List<String> oneFItemset = new ArrayList<String>();
for (Entry<String, Integer> entry : oneFrequentItemSetAndCount
.entrySet()) {
oneFItemset.add(entry.getKey()); // 将频繁一项集的各项事件存储到oneFItemset中
}
return oneFItemset;
}
/**
* 获取二项候选集
* @param oneFItemSet
* @return
*/
public static List<List<String>> findSencondCandidate(
List<String> oneFItemSet) { // 生成二项候选项集
List<List<String>> twoCandidateItemSet = new ArrayList<List<String>>();
for (int i = 0; i < oneFItemSet.size(); i++) { // 生成一半二项候选项集
for (int j = i + 1; j < oneFItemSet.size(); j++) {
List<String> tempList = new ArrayList<String>();
tempList.add(oneFItemSet.get(i));
tempList.add(oneFItemSet.get(j));
twoCandidateItemSet.add(tempList);
}
}
int halfTwoCandidateItemSize = twoCandidateItemSet.size(); // 二项候选项元素大小的一半
for (int i = 0; i < halfTwoCandidateItemSize; i++) { // 获取二项候选项集的另一半
List<String> tempList = new ArrayList<String>();
tempList.add(twoCandidateItemSet.get(i).get(1));
tempList.add(twoCandidateItemSet.get(i).get(0));
twoCandidateItemSet.add(tempList);
}
return twoCandidateItemSet;
}
/**
* 获取K项集中所有的频繁项
* @param cItemset
* @return
*/
public static List<List<String>> getSupportedItemset( // 从K〉=2开始计算事件数
List<List<String>> cItemset) {
// TODO Auto-generated method stub
System.out.print("进入getSupportedItemset");
boolean end = true;
int totalCount = 0;
int count = 0;
List<List<String>> supportedItemset = new ArrayList<List<String>>();
List<Integer> tempList = new ArrayList<Integer>();
for (int i = 0; i < cItemset.size(); i++) { // 对频繁项集中的每一项扫描整个事务库
totalCount = 0;
count = 0;
for (int j = 0; j < transactions.size(); j++) {
count = countSupportCount(transactions.get(j), cItemset.get(i)); //统计记录数
totalCount = totalCount + count;
}
if (totalCount > MIN_SUPPORT) { // count值大于规定阈值,即满足要求
tempList.add(totalCount); // 存储频繁项集中频繁项的事件数
supportedItemset.add(cItemset.get(i)); // supportedItemset存放频繁项集
end = false;
}
}
supporCount.add(tempList); // 存储每个频繁项集的事件数
endTag = end; // 挖掘终止与否
return supportedItemset;
}
/**
* 统计每一项的支持计数
* @param subsequence
* @param item
* @return
*/
private static int countSupportCount(List<String> subsequence,
List<String> item) {
// System.out.print("进入countSupportCount函数!");
int eventCount = 0; //中间计数
int supportCount = 0; //支持计数
Boolean flag = false;
List<Integer> eventIndex = new ArrayList<Integer>();
List<String> precursorEvent = new ArrayList<String>();
String postiorEvent = new String();
postiorEvent = item.get(item.size() - 1); // 获取后继事件
for (int k = 0; k < subsequence.size(); k++) {
if (subsequence.get(k).equals(postiorEvent)) { // 获取后继事件在事件子序列中的索引号
eventIndex.add(k);
}
}
if (eventIndex.size() == 0) { // 子序列中不包含后继事件
supportCount = 0;
} else {
for (int i = 0; i < item.size() - 1; i++) { // 获取前驱事件中的各个子事件
precursorEvent.add(item.get(i));
}
//两个事件之间的小区间是否存在前驱事件,若存在则事件数加
flag = ifExistPrecursorEvent(subsequence, precursorEvent, -1,
eventIndex.get(0));
if (flag == true) {
eventCount = 1;
} else {
eventCount = 0;
}
if (eventIndex.size() > 1) {
for (int j = 0; j < eventIndex.size()-1; j++) {
flag = ifExistPrecursorEvent(subsequence, precursorEvent,
eventIndex.get(j), eventIndex.get(j + 1));
if (flag == true) {
eventCount++;
}
}
}
supportCount = eventCount;
}
return supportCount;
}
/**
* 判断子区间内是否存在前驱事件,前驱事件为一项,两项.....
* @param subsequence 事务(子序列)
* @param precursorEvent 前驱事件
* @param beginIndex 子序列中一个区间的起始
* @param endIndex 子序列中一个区间的结束
* @return
*/
public static Boolean ifExistPrecursorEvent(List<String> subsequence,
List<String> precursorEvent, int beginIndex, int endIndex) {
Boolean flag = false;
Boolean havaThisItem = false;
if (beginIndex == endIndex
|| beginIndex == (endIndex - 1)
|| (endIndex - beginIndex) - 1 < precursorEvent
.size()) { // 若两个相同事件相邻,则区间不存在
flag = false;
return flag;
} else {
int i = endIndex;
int j = precursorEvent.size() - 1; // 记得减1,因为后面用的是索引
int k = 0;
// 若区间包含前驱事件则前去前驱事件序列中包含多少个事件,while循环就进行多少次,若不包含,则从后往前数,找到第一个不包含的就结束
while (j >= 0) { // j表示前驱事件中每个事件的索引号,从后向前依次搜索前驱事件的每个索引事件
havaThisItem = false;
for (k = i - 1; k > beginIndex; k--) { // 在子序列区间中找到前驱事件中的某个事件
if (precursorEvent.get(j).equals(subsequence.get(k))) {// 子序列中找到前驱事件中的某个事件
i = k;
havaThisItem = true;
break;
}
}
if (k == (beginIndex) && havaThisItem == false) { // 子序列区间扫描完,但在子序列中未找到前驱事件中的某个事件
flag = false;
break;
}
if (j == 0 && havaThisItem == true) { // 包含前驱事件中的第一项
flag = true;
}
j--;
}
}
return flag;
}
/**
* 获取二项频繁项集中所有的事件规则
* @param fkItemSet 二项集
* @param fItemSet 一项集
*/
public static void getSecondConfidencedItemset(
List<List<String>> fkItemSet, List<String> fItemSet) {
List<List<String>> tempList = new ArrayList<List<String>>();
double confidence = 0.0;
List<String> anotherTempList = new ArrayList<String>();
List<String> otherTempList = new ArrayList<String>();
for (int i = 0; i < fkItemSet.size(); i++) {
confidence = getSencondConfItem(fkItemSet.get(i), fkItemSet,
fItemSet);
anotherTempList.add(exportConfidence);
if (confidence > MIN_CONF) {
System.out.print("\r\n");
tempList.add(fkItemSet.get(i)); // lkItem.get(i)表示一个频繁项,为List<String>类型
otherTempList.add(exportConfidence);
for (String str : fkItemSet.get(i)) { // 输出满足置信度阈值的二项事件规则
System.out.print(str + " ");
}
System.out.print("打印二项事件规则" + "\r\n");
System.out.print(" " + "置信度为:" + exportConfidence + "\r\n");
}
}
satisfiedConfidence.add(otherTempList); // 存储二项事件规则的置信度显示
eventRules.add(tempList);
}
/**
* 获取二项频繁项集中某一项的置信度
* @param fkItem 二项集中的某一项
* @param fkItemSet 二项集
* @param fItemSet 一项集
* @return
*/
public static double getSencondConfItem(List<String> fkItem,
List<List<String>> fkItemSet, List<String> fItemSet) {
int currentItemNum = fkItemSet.get(0).size(); // 当前项集项数
int precursorItemNum = 1; // 前面项集项数
int oneItemPosition = 0; // 记录一项频繁项集的计数
double confidence = 0.0;
String precursorEvent = new String();
precursorEvent = fkItem.get(currentItemNum - 2); // 获取前驱事件
for (int i = 0; i < fItemSet.size(); i++) { // 获取前驱事件在其频繁项集中的索引
if (fItemSet.get(i).equals(precursorEvent)) {
oneItemPosition = i;
break;
}
}
int curerenEventPosition = findPosition(fkItem, fkItemSet); // 找当前事件(即二项集中各项)在其频繁项集中的索引
confidence = (double) (supporCount.get(currentItemNum - 1)
.get(curerenEventPosition))
/ supporCount.get(precursorItemNum - 1).get(oneItemPosition);
exportConfidence = Integer.toString(supporCount.get(currentItemNum - 1)
.get(curerenEventPosition))
+ "/"
+ Integer.toString(supporCount.get(precursorItemNum - 1).get(
oneItemPosition));
return confidence;
}
/**
* 获取K项集中满足置信度的频繁项集
* @param fkItemSet K项集 (K>2)
* @param fItemSet k-1项集
*/
public static void getConfidencedItemset(List<List<String>> fkItemSet,
List<List<String>> fItemSet) {
System.out.print(fkItemSet.size());
double confidence = 0.0;
List<List<String>> tempList = new ArrayList<List<String>>();
List<String> anotherTempList = new ArrayList<String>();
List<String> otherTempList = new ArrayList<String>();
for (int i = 0; i < fkItemSet.size(); i++) {
confidence = getConfItem(fkItemSet.get(i), fkItemSet, fItemSet);
anotherTempList.add(exportConfidence);
System.out.print(confidence);
if (confidence > MIN_CONF) {
tempList.add(fkItemSet.get(i));
otherTempList.add(exportConfidence);
for (String str : fkItemSet.get(i)) { // 输出满足置信度阈值的事件规则
System.out.print(str+" ");
}
System.out.print(" " + "置信度为:" + exportConfidence + "\r\n");
}
}
satisfiedConfidence.add(otherTempList); // 存储各个事件规则的置信度显示
eventRules.add(tempList);
}
/**
* 获取第K项集中某一项的置信度(k>2),List<String> fkItem为一个频繁项
* @param fkItem K项集中的某一项
* @param fkItemSet K项集
* @param fItemSet k-1项集
* @return
*/
public static double getConfItem(List<String> fkItem,
List<List<String>> fkItemSet, List<List<String>> fItemSet) {
int currentItemNum = fkItemSet.get(0).size(); // 当前项集项数
int precursorItemNum = fItemSet.get(0).size(); // 前面项集项数
double confidence = 0.0;
List<String> precursorEvent = new ArrayList<String>();
for (int i = 0; i < currentItemNum - 1; i++) { // 获取前驱事件
precursorEvent.add(fkItem.get(i));
}
int curerenEventPosition = findPosition(fkItem, fkItemSet); // 找当前事件在其所在频繁项集中的索引
int precursorEventPosition = findPosition(precursorEvent, fItemSet); // 找前驱事件在其所在频繁项集中的索引
confidence = ((double) supporCount.get(currentItemNum - 1).get(
curerenEventPosition))
/ supporCount.get(precursorItemNum - 1).get(
precursorEventPosition);
exportConfidence = Integer.toString(supporCount.get(currentItemNum - 1)
.get(curerenEventPosition))
+ "/"
+ Integer.toString(supporCount.get(precursorItemNum - 1).get(
precursorEventPosition));
return confidence;
}
/**
* 找到一个项在对应项集中的位置,项集大小大于等于2
* @param item 二项集中的某一项
* @param fItemSet
* @return
*/
public static int findPosition(List<String> item,
List<List<String>> fItemSet) {
int position = 0;
for (int i = 0; i < fItemSet.size(); i++) {
if (judgeEqual(item, fItemSet.get(i))) { // 判断前驱项是否和前驱项集中的项是否相同
position = i;
break;
}
}
return position;
}
/**
* 判断两个项是否相同
* @param item 一个项
* @param anotherItem 另一个项
* @return
*/
public static Boolean judgeEqual(List<String> item, List<String> anotherItem) { // 依次比较list中的每个项
Boolean flag = true;
for (int i = 0; i < item.size(); i++) {
if (item.get(i).equals(anotherItem.get(i))) {
flag = false;
break;
}
}
return flag;
}
/**
* 由K频繁项集获取K+1项候选集,通过自连接操作
* @param fItemset K项集,K>2
* @return
*/
public static List<List<String>> getNextCandidate(
List<List<String>> fItemset) {
List<List<String>> nextcItemset = new ArrayList<List<String>>();
Boolean flag = true;
System.out.print(fItemset.size() + "\r\n");
int ItemsetNum = fItemset.get(0).size(); // 项数K
for (int i = 0; i < fItemset.size(); i++) {
for (int j = 0; j < fItemset.size(); j++) { // 判断连接前项的后k-1项是否和连接后项的前k-1项是否相同
if (i != j) {
flag = true;
for (int m = 0; m < ItemsetNum - 1; m++) {
if (fItemset.get(i).get(m + 1).equals(fItemset.get(j).get(m))==false)// 含有不同项就终止
{
flag = false;
break;
}
}
if (true == flag
&& false==fItemset.get(i).get(0).equals(fItemset.get(j).get(
ItemsetNum - 1))) { // 含有相同项
List<String> tempList = new ArrayList<String>();
for (int k = 0; k < ItemsetNum; k++) {
tempList.add(fItemset.get(i).get(k));
}
tempList.add(fItemset.get(j).get(ItemsetNum - 1));
nextcItemset.add(tempList); // 得到新候选项集
}
}
}
}
return nextcItemset;
}
/**
* 打印所有的事件规则
*/
public static void print() {
System.out.print("打印侯选项集相关信息:\r\n");
// 打印事件规则的置信度
String eventRuleSupportFile = "/home/wdl/test/eventRuleConfidenceFile.txt";
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(new File(
eventRuleSupportFile), false));
for(int i=0;i<eventRules.size();i++){ //项集个数
for(int j=0;j<eventRules.get(i).size();j++){ //每一个项集中项的个数
for(int k=0;k<eventRules.get(i).get(j).size();k++){
System.out.print(eventRules.get(i).get(j).get(k)+"\t");
}
}
}
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
阅读全文
0 0
- 频繁数据集挖掘的一种实现方式
- 基于频繁项集的数据挖掘
- 数据挖掘---频繁项集挖掘Apriori算法的C++实现
- C++---Apriori算法实现,频繁模式数据挖掘,最大频繁项集,闭频繁项集
- 数据挖掘--频繁集测试--Apriori算法--java实现
- 数据挖掘频繁项集挖掘
- 数据挖掘之频繁项集分析
- 【数据挖掘】:闭频繁项集
- 海量数据挖掘MMDS week2: 频繁项集挖掘 Apriori算法的改进:非hash方法
- 数据挖掘中的模式发现(一)频繁项集、频繁闭项集、最大频繁项集
- 数据挖掘-关联分析频繁模式挖掘Apriori、FP-Growth及Eclat算法的JAVA及C++实现
- 数据挖掘-关联分析频繁模式挖掘Apriori、FP-Growth及Eclat算法的JAVA及C++实现
- 数据挖掘-关联分析频繁模式挖掘Apriori、FP-Growth及Eclat算法的JAVA及C++实现
- 海量数据挖掘MMDS week2: 频繁项集挖掘 Apriori算法的改进:基于hash的方法
- 数据挖掘之关联分析二(频繁项集的产生)
- 数据挖掘之关联分析二(频繁项集的产生)
- 数据挖掘之关联规则和频繁项集
- 数据挖掘_频繁项集/关联规则
- 用jQuery实现简单的加入收藏页面的功能
- 虚拟币开发专题(山寨币私钥、公钥、钱包地址之间的关系)
- Error:Execution failed for task ':hb_cippo:processDebugManifest'. > Manifest merger failed : Attribu
- jqGrid 各种参数详情!
- hdoj1305-Immediate Decodability
- 频繁数据集挖掘的一种实现方式
- 关于经典蓝牙和低功耗蓝牙的区别
- fork和vfork函数
- vue 相关技术文章地址和知识点整理
- 全备份、增量备份与差量备份
- [P1197][JSOI2008]星球大战
- Android 蓝牙4.0 BLE 获取链接设备Rssi值
- 使用phpize遇到问题
- Spring(17)——对JSR330标准的支持