可达用户投资额的计算(Java)
有话要说:
前阵子遇到了一个计算可达用户投资额的问题,觉得非常有趣,故把它记录下来。
问题描述:
某产品可被投资,注册后才可以投资,其注册可以被邀请(并不是每个人都是被邀请的)。邀请人可以邀请多个人注册投资,而被邀请人只能有一位邀请人。也就是说邀请人与被邀请人是一对多的关系。
现给定两组数据:邀请与被邀请的关系;所有用户的投资额。
如下:
1,2
1,4
2,11
2,10
4,5
5,6
6,8
6,9
6,7
13,14
13,15
13,17
14,20
20,21
15,18
15,19
以上数据是处理过后的邀请关系,前者邀请了后者。
1,1200.00
2,2300.00
4,1500.00
5,7300.00
6,4100.00
7,1100.00
8,9000.00
9,1000.00
10,1100.00
11,100.00
12,1000.00
13,4500.00
14,1100.00
15,1200.00
17,700.00
18,100.00
19,200.00
20,100.00
21,0.00
以上数据是处理过后的所有用户投资额。
现在根据这两组数据需要计算每位用户的“用户投资金额”,“可达用户投资金额”,“邀请用户阶数”,“成功邀请用户数”,“一阶推荐用户投资金额”。
用户投资金额:即该用户的投资额,以以上数据举个例子来说:2的投资金额为2300
可达用户投资金额:即该用户的投资额加上该用户邀请的所有人的投资额(包括该用户邀请人所邀请的人),假设A邀请了B和C,B邀请了D和E,C、D、E没有邀请别人,那么A的可达用户投资金额就是A、B、C、D、E投资额的总数
邀请用户阶数:即该用户所邀请人的总数(不包括该用户邀请人所邀请的人),假设A邀请了B和C,B邀请了D和E,C、D、E没有邀请别人,那么A的邀请用户阶数为2
成功邀请用户数:在邀请用户阶数的基础上除去投资额为零的用户
一阶推荐用户投资金额:即该用户所邀请人的总投资额(不包括该用户邀请人所邀请的人),假设A邀请了B和C,B邀请了D和E,C、D、E没有邀请别人,那么A的一阶推荐用户投资金额为B、C投资额的总数
大家可以先想想如何处理该数据
思路分析:
首先根据邀请的关系可以分析出邀请人的关系图可以分解为多棵不相关的树(反证法可证)。
邀请关系如下图所示:

因此可以先将数据生成若干棵树,然后针对于树进行遍历。
对于每棵树来说,父节点的可达用户投资金额就是子节点的可达用户投资金额之和再加上父节点的投资额,根据这一特性,可以对每棵树从叶子节点网上遍历。
代码实现:
获取所有用户的投资额:
直接读取文件,并把用户id与用户投资额放在Map集合中
// 获取每个人的投资额
public static Map<String, Double> getAmount() {
Map<String, Double> amounts = new HashMap<String, Double>(); File file = new File("D:/amount.txt"); try {
// 创建输入流,读取txt
InputStream is = new FileInputStream(file.getAbsolutePath());
InputStreamReader isr = new InputStreamReader(is, "UTF-8"); BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null) {
String[] amount = line.trim().split(",");
amount[0] = amount[0].trim();
amounts.put(amount[0], Double.parseDouble(amount[1]));
} br.close();
isr.close();
is.close(); } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return amounts;
}
获取用户的邀请关系:
直接读取文件,并把用户的邀请关系放在List集合中
// 获取关系数据集合
public static List<String[]> getRelationship() {
List<String[]> list = new ArrayList<String[]>(); File file = new File("D:/relationship.txt"); try {
// 创建输入流,读取txt
InputStream is = new FileInputStream(file.getAbsolutePath());
InputStreamReader isr = new InputStreamReader(is, "UTF-8"); BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null) {
String[] relation = line.trim().split(",");
relation[0] = relation[0].trim();
relation[1] = relation[1].trim();
list.add(relation);
} br.close();
isr.close();
is.close(); } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
构造Person对象:
// 用户邀请关系(前者邀请了后者)
List<String[]> relationships = new ArrayList<>();
relationships = getRelationship(); // 每个用户投资额
Map<String, Double> amounts = new HashMap<String, Double>();
amounts = getAmount(); // 构造用户信息
Map<String, Person> persons = new HashMap<String, Person>();
Iterator<String> it = amounts.keySet().iterator();
while (it.hasNext()) {
String key = it.next(); Person temp = new Person();
temp.setId(key);
temp.setAmount(amounts.get(key)); persons.put(key, temp);
}
其中Person类为:
class Person {
/**
* 用户id
*/
private String id;
/**
* 邀请人列表
*/
private List<Person> invitedPersons = new ArrayList<Person>();
/**
* 投资额
*/
private double amount;
/**
* 所有下线投资额总额(包括自身)
*/
private double allAmount;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public List<Person> getInvitedPersons() {
return invitedPersons;
}
public void setInvitedPersons(List<Person> invitedPersons) {
this.invitedPersons = invitedPersons;
}
public double getAllAmount() {
return allAmount;
}
public void setAllAmount(double allAmount) {
this.allAmount = allAmount;
}
}
构建邀请关系:
for (int i = 0; i < relationships.size(); i++) {
String[] relationship = relationships.get(i);
// 获取邀请人
Person person1 = persons.get(relationship[0]);
// 获取被邀请人
Person person2 = persons.get(relationship[1]);
// 加上关联关系
person1.getInvitedPersons().add(person2);
}
根据以上的构造,已经针对于每个用户都生成了一棵树。
但事实上这些树有大量的重合,如果逐一遍历的话会导致不必要的浪费。
于是准备找出所有的根树,遍历根数即可。
找到所有的根树:
// 所有person集合
List<Person> allPerson = new ArrayList<>();
allPerson.addAll(persons.values()); // 找到根节点,去除非根节点
for (int i = 0; i < relationships.size(); i++) {
String[] relationship = relationships.get(i); // 如果是被邀请人,则去除
persons.remove(relationship[1]);
}
根据以上精简,persons集合里只有根节点。
遍历根数,找出所有人的可达用户投资金额:
Iterator<Person> it4 = persons.values().iterator();
while (it4.hasNext()) {
Person person = it4.next();
addAllChildren(person);
}
其中addAllChildren为
// 可达用户投资金额
public static double addAllChildren(Person person) {
double childrenAmount = 0; // 先加自己的
childrenAmount += person.getAmount(); // 再加孩子的
for (int i = 0; i < person.getInvitedPersons().size(); i++) {
Person child = person.getInvitedPersons().get(i); if (child.getInvitedPersons().size() > 0) {
childrenAmount += addAllChildren(child);
} else {
childrenAmount += child.getAmount();
}
} person.setAllAmount(childrenAmount); return childrenAmount;
}
通过此遍历,每个person对象的可达用户投资金额均已被算出,又因为java是值传递的,所以allPerson内的person对象的值都已经被计算出来了。
接下来就可以遍历所有的person对象了。
遍历person集合,算出需要的所有数据:
try {
String path = "D:/result.txt";
File file = new File(path);
if (!file.exists()) {
file.getParentFile().mkdirs();
}
file.createNewFile();
// write
FileWriter fw = new FileWriter(file, true);
BufferedWriter bw = new BufferedWriter(fw);
Iterator<Person> it3 = allPerson.iterator();
while (it3.hasNext()) {
Person person = it3.next();
if (person.getAllAmount() == 0) {
bw.write("可达用户投资总额:" + person.getAmount() + ";");
} else {
bw.write("可达用户投资总额:" + person.getAllAmount() + ";");
}
bw.write("邀请用户阶数:" + person.getInvitedPersons().size() + ";");
int count = 0;
double sum = 0;
for (Person temp : person.getInvitedPersons()) {
if (temp.getAmount() != 0) {
sum += temp.getAmount();
count++;
}
}
bw.write("成功邀请用户数:" + count + ";");
bw.write("一阶推荐用户投资金额:" + sum + ";\n");
}
bw.flush();
bw.close();
fw.close();
} catch (Exception e) {
e.printStackTrace();
}
结束语:
以上便是我这次遇到问题的解决思路以及解决过程,对于树的构造以及java值传递的应用值得学习。
本题的解决思路以及解决过程比较仓促,只用了一下午的时间,可能会有更好的解决办法,如果大家有什么更好的解决办法欢迎留言~
大家如果有什么疑问或者建议可以通过评论或者邮件的方式联系我,欢迎大家的评论~
可达用户投资额的计算(Java)的更多相关文章
- 计算Java对象内存大小
摘要 本文以如何计算Java对象占用内存大小为切入点,在讨论计算Java对象占用堆内存大小的方法的基础上,详细讨论了Java对象头格式并结合JDK源码对对象头中的协议字段做了介绍,涉及内存模型.锁原理 ...
- Win7网上邻居提示未授予用户在此计算机上的请求登录类型解决办法
内容简介 装了Win7之后很多人遇到这样的问题,网上邻居访问Win7的电脑时出现“未授予用户在此计算机上的请求登录类型”问题.打开“控制面板”--“管理工具”--“本地安全策略”--“本地策略 ...
- windows 7 共享,未授予用户在此计算机上的请求登录类型
刚刚重装了windows7,新下载的一个ghost版本,结果却不能共享,每次访问这台机器的共享都提示, 未授予用户在此计算机上的请求登录类型 这个情况好像是存在于win7访问win7,我用一台XP系统 ...
- win7基于mahout推荐之用户相似度计算
http://www.douban.com/note/319219518/?type=like win7基于mahout推荐之用户相似度计算 2013-12-03 09:19:11 事情回到半年 ...
- 如何准确计算Java对象的大小
如何准确计算Java对象的大小 原创文章,转载请注明:博客园aprogramer 原文链接:如何准确计算Java对象的大小 有时,我们需要知道Java对象到底占用多少内存,有人通过连续调用两 ...
- 两种计算Java对象大小的方法
之前想研究一下unsafe类,碰巧在网上看到了这篇文章,觉得写得很好,就转载过来.原文出处是: http://blog.csdn.net/iter_zc/article/details/4182271 ...
- Ehcache计算Java对象内存大小
在EHCache中,可以设置maxBytesLocalHeap.maxBytesLocalOffHeap.maxBytesLocalDisk值,以控制Cache占用的内存.磁盘的大小(注:这里Off ...
- 简单复利计算java板
一.要求: 1.客户说:帮我开发一个复利计算软件. 2如果按照单利计算,本息又是多少呢? 3.假如30年之后要筹措到300万元的养老金,平均的年回报率是3%,那么,现在必须投入的本金是多少呢? 4.利 ...
- CCF 201612-2 工资计算 java 解题
问题描述 小明的公司每个月给小明发工资,而小明拿到的工资为交完个人所得税之后的工资.假设他一个月的税前工资(扣除五险一金后.未扣税前的工资)为S元,则他应交的个人所得税按如下公式计算: 1) 个人所得 ...
随机推荐
- Java集合详解8:Java集合类细节精讲
今天我们来探索一下Java集合类中的一些技术细节.主要是对一些比较容易被遗漏和误解的知识点做一些讲解和补充.可能不全面,还请谅解. 本文参考:http://cmsblogs.com/?cat=5 具体 ...
- HBase的下载、安装与配置
声明:作者原创,转载注明出处. 作者:帅气陈吃苹果 (一)安装 下载.解压: wget http://mirror.bit.edu.cn/apache/hbase/1.3.1/hbase-1.3.1- ...
- pytest-allure-poco之allure全量详细用法
allure简介 Allure Framework是一个灵活的轻量级多语言测试报告工具.貌似是目前最漂亮的一个报告工具 python版本及必要库或工具 python 3.7 pytest 4.3.1 ...
- spring boot redis 数据库缓存用法
缓存处理方式应该是 1.先从缓存中拿数据,如果有,直接返回.2.如果拿到的为空,则数据库查询,然后将查询结果存到缓存中.由此实现方式应该如下: private String baseKey = &qu ...
- 简述private,protected,public,internal修饰符的访问权限
private:私有成员,在类的内部才可以访问 protected:保护成员,在类的内部和继承类中可以访问 public:公共成员,完全公开,没有访问限制 internal:当前程序集内可以访问
- java技术栈范畴
基础 进阶 数据 安全
- solr的认识、linux下安装、java下使用(含下载资源)
目录 一.solr的大概认识 二.solr安装 三.solr的深度认识 四.solr的使用 (1)由于我们用到中文,所以需要中文分析器,这里我用IK Analyzer 2012FF_hf1 (2)同时 ...
- css样式的继承性、层叠性 、优先级
一.css样式的继承性: 作用:给父元素设置一些属性,子元素也可以使用 应用场景: 一般用于设置网页上的一些共性信息,例如网页的文字颜色,字体,文字大小等内容.优化代码,降低工作量 注意点: 1.并不 ...
- MySQL 多实例的安装
Step 1 . 准备工作 关闭防火墙; 安装 MySQL依赖于libaio 库:下载,解压,重命名 MySQL的执行文件:新建用户组和用户 等准备工作在此不再赘述. 可执行文件目录为 /data/m ...
- 听说你的MES系统又失败了?
前些日子,一位前同事跟我抱怨,他们做的MES系统,凉凉了.这样的话,我从不同人口中听到过不止一次. 我们做的系统,做到一半做不下去了...... 我们的系统,工人都不爱用...... 不只是MES,所 ...