对象和枚举的反序列化
在单例模式中,我们会存在序列化破坏单例模式的问题,但是我们可以通过用枚举类保证。因为枚举类天生就是满足单例模式的。所以下面就总结一下序列化对于对象和枚举的关系。
枚举和对象的反序列化我们主要使用ObjectInputStream的readObject方法:
在源码中调用了Object obj = readObject0(type, false);来读取对象。
12345678910111213141516171819202122private final Object readObject(Class<?> type) throws IOException, ClassNotFoundException{ ... try { Object obj = readObject0(type, false); handles.markDependency(outerHandle, passHandle); ClassNotFoundException ex = handles.lookupExcepti ...
最大公约数
,m,其中 a<m。
请你计算,有多少个小于 m 的非负整数 x 满足:
gcd(a,m)=gcd(a+x,m)
输入格式第一行包含整数 T,表示共有 T 组测试数据。
每组数据占一行,包含两个整数 a,m。
输出格式每组数据输出一行结果,一个整数,表示满足条件的非负整数 x 的个数。
数据范围前三个测试点满足,1≤T≤10。所有测试点满足,1≤T≤50,1≤a<m≤1010。
输入样例:123434 95 1042 9999999967
输出样例:123619999999966
题目思路我们首先需要化简题目:题目求的其实是 有多少x满足 x 大于零,且小于m,并且(a+x)/gcd(a,m)和m/gcd(a,m)互质。
这个题目我们可以分部分化简,
a + x <= m
这个情况下,我们其实就是求 a / gcd(a,m) 到 (a+m)/gcd(a,m) 中间有多少数字和(a+m)/gcd(a,m)互质。
a + x > m
这个情况我们求(a+m)/gcd(a,m) ...
博弈论
NIM游戏题目描述给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式第一行包含整数 n。
第二行包含 n个数字,其中第 i 个数字表示第 i 堆石子的数量。
输出格式如果先手方必胜,则输出 Yes。
否则,输出 No。
数据范围1≤n≤105,1≤每堆石子数≤109
输入样例:1222 3
输出样例:1Yes
解题思路:首先介绍一个概念就是必胜态和必败态。这都是先手状态。必胜态表示某种方式可以转化成必败态。必败态表示不管怎么操作的状态都是必胜态。
先给出这个题目的结论:
所有石子的异或为0,那么就是必败态
所有石子的异或非0,那么就是必胜态
需要证明,为什么异或非0可以转化成异或为0?首先,我们假设异或结果为x。x的二进制最高位为k。那么在石堆中肯定含有大于2^(k-1)的石堆。我们假设这堆式子为ai个。那么我们可以知道ai ^ x 一定小于 ai(不管k位后怎么变,k位变0了)。因为ai ^ x < ai,所以我们可以从石堆ai中拿 ...
扑克牌
题目描述Admin 生日那天,Rainbow 来找 Admin 玩扑克牌。
玩着玩着 Rainbow 觉得太没意思了,于是决定给 Admin 一个考验。
Rainbow 把一副扑克牌(5454 张)随机洗开,倒扣着放成一摞。
然后 Admin 从上往下依次翻开每张牌,每翻开一张黑桃、红桃、梅花或者方块,就把它放到对应花色的堆里去。
Rainbow 想问问 Admin,得到 A张黑桃、B 张红桃、C 张梅花、D 张方块需要翻开的牌的张数的期望值 E 是多少?
特殊地,如果翻开的牌是大王或者小王,Admin 将会把它作为某种花色的牌放入对应堆中,使得放入之后 E的值尽可能小。
由于 Admin 和 Rainbow 还在玩扑克,所以这个程序就交给你来写了。
输入格式输入仅由一行,包含四个用空格隔开的整数,A,B,C,D。
输出格式输出需要翻开的牌数的期望值 E,四舍五入保留 33 位小数。
如果不可能达到输入的状态,输出 -1.000。
数据范围0≤A,B,C,D≤15
输入样例:11 2 3 4
输出样例:116.393
解题思路我们可以把这个题目,翻每一张牌的过程,抽象到一个状态到另 ...
绿豆的归宿
题目描述给出一个有向无环的连通图,起点为 1,终点为 N,每条边都有一个长度。
数据保证从起点出发能够到达图中所有的点,图中所有的点也都能够到达终点。
绿豆蛙从起点出发,走向终点。
到达每一个顶点时,如果有 K 条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 1/K。
现在绿豆蛙想知道,从起点走到终点所经过的路径总长度的期望是多少?
输入格式第一行: 两个整数 N,M代表图中有 N 个点、M 条边。
第二行到第 1+M 行: 每行 33 个整数 a,b,c代表从 a到 b有一条长度为 c 的有向边。
输出格式输出从起点到终点路径总长度的期望值,结果四舍五入保留两位小数。
数据范围1≤N≤105,1≤M≤2N
输入样例:123454 41 2 11 3 22 3 33 4 4
输出样例:17.00
解题思路如下图有向无环图,其中黑色为点的编号,红色为路径长度。
我们可以知道1->3->5->7 的一条路径对期望的贡献是 $\frac{1}{3} \times \frac{1}{2} \times 1 \times (2 + 3 ...
矩阵乘法
矩阵乘法矩阵乘法的简单实现我们实现一个简单的两个矩阵相乘,A*B , B*C -- > A*C。
伪代码如下:
1234for(int i = 1;i <= A;i++) for(int j = 1;j <= C;j++) for(int k = 1;k <= B;k++) C[i][j] += A[i][k] * B[k][j];
矩阵乘法结合快速幂一般来说我们矩阵会有AE^n的形式,我们E一般是两个维度都相同,根据矩阵的结合律,我们可以先根据快速幂求出E^n,在左乘A的到结果。
例如题目斐波那契
12345678910111213141516171819202122232425262728293031323334353637383940414243import java.io.*;import java.util.*;class Main{ public static Scanner sc = new Scanner(System.in); public static int[][] a ...
kafka高水位和LeaderEpoch
在kafka中我们高水位和leader epoch机制是用来解决kafka主从之间的消息同步问题。下面分两部分进行介绍
高水位在Kafka中,高水位(High Watermark,HW)标志着消费者能够安全读取的消息的最高偏移量。每个分区的高水位是保证数据一致性的关键机制之一,它表示所有同步副本(ISR)已经复制的消息的最小偏移量。我们可以通过下面的步骤来解释高水位如何实现同步:
消息写入:
生产者(Producers)向领导副本(Leader)发送消息。
领导副本将消息追加到它的日志中,并更新其本地日志的最后偏移量(Last Offset)。
消息复制:
领导副本负责将这些新消息复制到所有的跟随副本(Followers)。
跟随副本将复制的消息追加到自己的日志中,并向领导副本发送确认(ACK)。
计算高水位:
领导副本收到所有同步副本对特定消息的确认后,它将更新高水位。
高水位被设定为所有同步副本中已复制数据的最小偏移量。换句话说,高水位是所有跟随副本都确认接收到的最小偏移量。
消费者读取:
消费者(Consumers)从领导副本读取消息。
为了确保消费者不会读 ...
kafka中subscribe和assign
在Apache Kafka中,subscribe和assign是两种不同的方式来消费Kafka主题中的消息。这两种方法在功能和使用场景上有所不同:
1. Subscribe (订阅)
动态分区分配:使用subscribe方法,消费者可以订阅一个或多个主题,并且由Kafka消费者群组协调器自动分配分区。这意味着,如果在消费者群组中增加或减少消费者,Kafka会重新平衡分区,自动分配给群组中的消费者。
适用场景:当你希望多个消费者平等地共享处理一个主题的消息负载,而且消费者的数量可能会动态变化时,subscribe是更合适的选择。
灵活性:subscribe允许消费者根据主题名字模式动态地订阅主题,而不是事先指定具体的分区。这为消费者提供了更高的灵活性,尤其是在主题数量可能变化的场景下。
2. Assign (分配)
静态分区分配:使用assign方法,消费者可以直接分配一个或多个特定的分区。这意味着,消费者直接确定要消费的分区,而不是订阅主题。这种方式不涉及Kafka的消费者群组协调器,因此不会自动进行分区的重新平衡。
适用场景:assign适用于那些需要精细控制消费特定分区数据的场景 ...
kafka分区
在Kafka中,消息是在主题(topic)中存储的,而每个主题可以被划分为多个分区(partitions)。Kafka通过分区来实现数据的并行处理,以及增强数据吞吐量和可扩展性。默认情况下,Kafka提供了自己的分区策略来决定每条消息被发送到哪个分区,但在某些场景下,使用自定义分区器(partitioner)来覆盖默认的分区逻辑是非常有用的。下面列出了几个为什么需要自定义分区器的原因:
1. 更细粒度的控制默认的分区逻辑可能不足以满足特定的业务需求。通过实现自定义分区器,开发者可以根据消息内容或其他业务逻辑来决定消息应该被发送到哪个分区,从而实现更细粒度的控制。
2. 负载均衡尽管Kafka的默认分区策略已经能够在大多数情况下实现负载均衡,但在某些特定的消息流模式下,可能会出现某些分区负载过高而其他分区负载过低的情况。通过自定义分区逻辑,可以更好地分散消息到不同的分区中,避免某些分区成为热点,从而更有效地利用集群资源。
3. 保证消息顺序在某些应用场景中,保持消息的顺序性是非常重要的,尤其是当处理与特定键相关的消息时。Kafka保证在同一个分区内的消息是有序的。通过自定义分区器,开发者 ...
kafka的segment存储
fka的Segment(分段)是指分区中的日志被分成的一个个较小的文件。这种设计使得Kafka能够有效地管理存储和提供高性能的数据访问。Kafka中的Segment文件对日志数据的管理是非常关键的。Segment文件的组成让Kafka既能高效存储大量消息,也能快速地检索到特定的消息。这里是如何做到的:
Segment文件的两大组成部分:
日志文件(.log):这个文件是用来存储实际的消息数据。当生产者发送消息到Kafka的某个分区时,这些消息会被追加到当前活跃的Segment的日志文件的末尾。每个消息都有一个唯一的偏移量(Offset),这个偏移量在分区内是连续的。消息在日志文件中是按照它们被接收的顺序存储的,这保证了在同一个分区内,消息的存储顺序与发送顺序一致。
索引文件(.index):为了能够快速地定位到日志文件中的特定消息,Kafka为每个Segment创建了一个索引文件。索引文件存储了消息的偏移量和对应消息在日志文件中的物理位置(比如字节偏移量)。这种设计允许Kafka在不必扫描整个日志文件的情况下,快速查找到特定偏移量的消息,从而大大提高了消息检索的速度。
工作原理: ...