国密算法 java实现_Java 实现贪心算法实例介绍

本文介绍如何使用Java实现贪心算法解决社交网络数据提取问题,通过对比贪心算法与传统穷举方法,展示了贪心算法在面对API调用限制时的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(给ImportNew加星标,提高Java技能)

编译:ImportNew/覃佑桦

www.baeldung.com/java-greedy-algorithms

1.引言

本文将介绍如何用 Java 实现贪心算法。

2.贪心问题

通常一个数学问题会有几种解法。可以迭代解决,也可以用分治法(快速排序算法)或者动态规划(背包问题)这样的高级方法。

大多数情况我们都在寻找最优解,但可悲的是并非每次都能得到期望的结果。某些情况下,即使是次优解也很有用。借助一些策略或者启发式方法,我们能得到这样的宝贵结果。

假定问题可以分解,并且在过程的每一步都选择局部最优即“贪心选择”,那么可以称为贪心算法。

这里的重点是问题“可拆分”:即问题可以被描述为一组相似的子问题。大多数情况下,贪心算法都采用递归实现。

即使有诸多限制,像计算资源限制、限制执行时间、API 或其它限制,贪心算法仍然有办法找到合理的解决方案。

2.1.问题

作为示例,接下来会实现一个贪心策略通过 API 从社交网络提取数据。

假设我们希望在社交网络“小蓝鸟”上吸引更多的用户。要达成目标最好的方法是发布原创内容或者转发,引起大家的兴趣。

如何找到感兴趣的观众呢?好吧,我们必须找到一个流量大 V,为他们发布一些内容。

2.2.经典算法 vs 贪心算法

考虑下面的情况:如下图所示,大 V 帐户有4个粉丝,每个粉丝分别又有2个、2个、1个和3个关注者,以此类推:

b9fcd0654320778532e9788b0304762d.png

带着之前的目标,我们会挑选粉丝较多的关注者。接下来把这个过程重复两次以上,直到抵达第3级(共4级)。

通过这种方式我们确定了一组粉丝最多的用户。如果可以向这组用户推荐,那么他们中一定会有人打开页面。

先从“传统方式”开始。每一次都查询关注此帐户的粉丝。随着选择地进行,查询的帐户数量会逐渐增加。

令人吃惊的是,总共要执行25次查询:

ba38b292f4b137b1dbbe8b352fe2383b.png

这里有一个问题:Twitter API 的查询限制是每15分钟最多查询15次。如果超过调用限制,则会报告 “Rate limit exceeded code – 88” 或者“API v1.1 由于应用程序 rate limit 达到限值无法处理请求”。如何突破限制?

远在天边,近在眼前:贪心算法。在贪心算法中,每一步只有粉丝最多的用户才是我们考虑的对象:结果只需要4次查询,效果显著!

f1803cf7945c2882bbf2c6ec2feebbba.png

这两种方法的结果将有所不同。使用传统方法,最好的情况下需要16次查询,而贪心算法最多需要12次。

这种差异是否真的有价值?这个稍后再讨论。

3.实现

为了实现上述逻辑,我们做了一个 Java 程序模拟 Twitter API。此外,还用到了Lombok 开发库。

定义SocialConnector 实现代码逻辑。注意:包含的 counter 模拟调用限制,为方便测试把限制减少到4次:

public class SocialConnector {
private boolean isCounterEnabled = true;
private int counter = 4;
@Getter @Setter
List users;

public SocialConnector() {
users = new ArrayList<>();
}

public boolean switchCounter() {
this.isCounterEnabled = !this.isCounterEnabled;
return this.isCounterEnabled;
}
}

接着添加 getFollowers 方法获取粉丝列表:

public List getFollowers(String account) {
if (counter < 0) {
throw new IllegalStateException ("API limit reached");
} else {
if (this.isCounterEnabled) {
counter--;
}
for (SocialUser user : users) {
if (user.getUsername().equals(account)) {
return user.getFollowers();
}
}
}
return new ArrayList<>();
}

为了流程需要,还需要添加 User 实体类:

public class SocialUser {
@Getter
private String username;
@Getter
private List followers;
@Overridepublic boolean equals(Object obj) {return ((SocialUser) obj).getUsername().equals(username);
}public SocialUser(String username) {this.username = username;this.followers = new ArrayList<>();
}public SocialUser(String username, List followers) {this.username = username;this.followers = followers;
}public void addFollowers(List followers) {this.followers.addAll(followers);
}
}

3.1.贪心算法

最后实现贪心算法,添加 GreedyAlgorithm 开始实现递归:

public class GreedyAlgorithm {
int currentLevel = 0;
final int maxLevel = 3;
SocialConnector sc;
public GreedyAlgorithm(SocialConnector sc) {
this.sc = sc;
}
}

需要添加一个 findMostFollowersPath 方法,找到粉丝最多的用户并对其进行计数,然后继续执行下一步:

public long findMostFollowersPath(String account) {
long max = 0;
SocialUser toFollow = null;

List followers = sc.getFollowers(account);
for (SocialUser el : followers) {
long followersCount = el.getFollowersCount();
if (followersCount > max) {
toFollow = el;
max = followersCount;
}
}
if (currentLevel < maxLevel - 1) {
currentLevel++;
max += findMostFollowersPath(toFollow.getUsername());
}
return max;
}

请记住:在这里进行贪心选择。每次调用 findMostFollowersPath  时,从列表中仅选择一个元素并继续:已选择的路径不再返回。

完美!准备就绪,可以开始测试我们的程序。在此之前需要初始化迷你关系网,然后启动单元测试:

public void greedyAlgorithmTest() {
GreedyAlgorithm ga = new GreedyAlgorithm(prepareNetwork());
assertEquals(ga.findMostFollowersPath("root"), 5);
}

3.2.非贪心算法

创建一个非贪心方法,看看执行效果。新建 NonGreedyAlgorithm 类:

public class NonGreedyAlgorithm {
int currentLevel = 0;
final int maxLevel = 3;
SocialConnector tc;

public NonGreedyAlgorithm(SocialConnector tc, int level) {
this.tc = tc;
this.currentLevel = level;
}
}

实现一个功能等价的 findMostFollowersPath 方法:

public long findMostFollowersPath(String account) {
List followers = tc.getFollowers(account);long total = currentLevel > 0? followers.size() : 0;if (currentLevel < maxLevel ) {
currentLevel++;long[] count = new long[followers.size()];int i = 0;for (SocialUser el : followers) {
NonGreedyAlgorithm sub = new NonGreedyAlgorithm(tc, currentLevel);
count[i] = sub.findMostFollowersPath(el.getUsername());
i++;
}long max = 0;for (; i > 0; i--) {if (count[i-1] > max) {
max = count[i-1];
}
}return total + max;
}return total;
}

准备相应的单元测试:一个用来验证是否超出调用限制,另一个用来检查使用非贪心算法的返回值:

public void nongreedyAlgorithmTest() {
NonGreedyAlgorithm nga = new NonGreedyAlgorithm(prepareNetwork(), 0);
Assertions.assertThrows(IllegalStateException.class, () -> {
nga.findMostFollowersPath("root");
});
}

public void nongreedyAlgorithmUnboundedTest() {
SocialConnector sc = prepareNetwork();
sc.switchCounter();
NonGreedyAlgorithm nga = new NonGreedyAlgorithm(sc, 0);
assertEquals(nga.findMostFollowersPath("root"), 6);
}

4.结果

回顾一下前面的工作。

首先,我们尝试了贪心算法并验证了其有效性。然后,在启用和取消 API 限制的情况下验证了传统方法的结果(穷尽式搜索)。

贪心算法能快速返回结果,每次都选择局部最优解。相反,由于 API 限制,非贪心算法可能无法返回结果(抛异常)。

比较两种方法及结果可以看到,即使得到的不是最优解,贪心算法也能为我们解忧。可以把它称为局部最优解。

5.总结

在社交媒体瞬息万变今天,找到最优解也许已经成为一种不切实际的妄想:既难以达到,也不太现实。

如何克服限制与优化 API 调用是一个很大的课题,但正如本文讨论的那样,贪心算法是一种有效的办法。选择贪心算法能帮助我们减轻寻找最优解的痛苦,进而得到有价值的结果。

请记住,贪心算法也非银弹,不是每种情况都适用:需要具体问题具体分析。

本文中的示例代码可在 GitHub 上找到。

github.com/eugenp/tutorials/tree/master/algorithms-miscellaneous-5

推荐阅读   点击标题可跳转

Java 异步编程:Loom 项目介绍

Java8 的 Stream 对集合操作飞起来

2020 年最棒的 9 个 Java 框架,哪个最香?

看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

5b17f2d88468a67c9258798afe30d80b.png

好文章,我在看❤️

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值