当前位置: 首页 > news >正文

IP段转CIDR:原理Java实现

🧑 博主简介:CSDN博客专家历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
技术合作请加本人wx(注明来自csdn):foreast_sea

在这里插入图片描述


在这里插入图片描述

IP段转CIDR:Java实现

首先,需要确认CIDR是什么?CIDR表示的是无类别域间路由,通常形式是IP地址后跟一个斜杠和数字,比如192.168.1.0/24。这个数字表示网络前缀的位数,剩下的位数用于主机地址。CIDR的作用是更高效地分配IP地址,减少浪费。

我们现在要实现,将给定的起始IP结束IP转换为CIDR块。那么,如何从两个IP地址中找到覆盖它们的CIDR呢?可能需要分解成多个CIDR块,因为单个CIDR可能无法覆盖整个范围,尤其是当范围不是连续的2的幂次方时。

一、需要考虑以下几点

  1. 将IP地址转换为整数:IPv4地址是32位的,可以将每个部分转换为二进制,然后组合成一个长整数,这样便于计算。例如,192.168.1.0转换为整数是3232235776。

  2. 确定起始和结束IP的整数形式:需要将输入的字符串拆分四个部分,每个部分转换为整数,并进行位移组合成一个长整型。

  3. 计算CIDR块:从起始地址开始,找到最大的前缀长度(即最小的子网),使得该子网包含在起始和结束范围内。然后,将起始地址递增到下一个子网的起始地址,重复这个过程直到覆盖整个范围。

这里的关键步骤是如何找到每个可能的CIDR块。例如,假设起始IP是192.168.0.0,结束IP是192.168.3.255。这个范围可以被分解为192.168.0.0/23或者192.168.0.0/24192.168.1.0/24以及192.168.2.0/23等,但需要找到最合适的分解方式。

二、可能的算法步骤

  • 将起始IP和结束IP转换为长整数。
  • 如果起始IP大于结束IP,则交换它们。
  • 初始化一个列表来保存CIDR块。
  • 循环处理,直到起始IP超过结束IP:
  • 计算起始IP的最低位有效位(即最大的后缀0的位数),这决定了当前可能的最大CIDR块。
  • 同时,确定当前块的大小,不超过结束IP。
  • 确定实际可以使用的掩码位数,使得该子网不超过结束IP。
  • 将CIDR块添加到列表。
  • 将起始IP加上当前块的大小,继续处理。

这里需要注意的是,每次找到的CIDR块应该是最大的可能块,以减少块的数量。例如,如果起始IP是192.168.0.0,结束IP是192.168.0.255,那么CIDR/24。但如果结束IP是192.168.1.127,可能需要分解为多个块,如/24和/25等。

在Java中实现时,需要处理大整数,因为IPv4是32位,可以使用long类型来避免符号问题。同时,掩码的计算可能需要位运算,比如找出最长的公共前缀。

另外,还需要处理一些特殊情况,比如起始和结束IP相同,这时候CIDR就是/32。或者起始IP到结束IP是一个连续的块,但无法被一个CIDR覆盖,需要拆分成多个。

例如,起始IP是192.168.0.5,结束IP是192.168.0.7。这时候CIDR列表应该是192.168.0.5/32, 192.168.0.6/31,或者类似的组合。但需要确保每个CIDR块覆盖的IP范围在起始和结束之间。

三、Java程序实现步骤

现在,需要将这些逻辑转化为Java代码,需要以下步骤:

  1. 编写一个方法将IP字符串转换为长整数

  2. 编写一个方法将长整数转换回IP字符串

  3. 编写方法计算两个IP之间的CIDR块列表

在转换IP到长整数时,需要将每个部分左移相应的位数,例如第一个部分左移24位,第二个左移16位,依此类推。同时,确保每个部分的数值在0-255之间。

在计算CIDR块时,需要找到最大的掩码位数,使得该子网的起始地址加上块大小不超过结束IP。块大小是2^(32 - mask),所以需要找到最大的mask,使得start + size <= end + 1。或者,另一种方式是找到起始IP的最低位有效位,然后逐步调整掩码位数。

3.1 如何计算mask?

可以计算从start到end的差值为diff = end - start +1。然后,最大的块是最大的2的幂次方,不大于diff,并且块的大小还要考虑start的低位是否为0。

或者,找到从start开始的最低位有效位,例如,计算start的二进制中最低位的1的位置,然后块的大小是2^该位置,但是不能超过end - start +1。

例如,start的二进制是x,那么最大的块大小是最大的2的幂次方,使得块大小 <= diff,并且块大小是start的二进制中最低位1所代表的值。

例如,假设start是192.168.0.5(转换为整数是3232235525),二进制是…00000101。最低位的1的位置是0(因为最后一位是1),所以块大小是1,即mask是32。这样该块只包含start自己。但如果diff大于1,那么可能之后的下一个块可以更大。

或者,这可能不是最优的方式。正确的做法应该是找到最大的mask,使得该子网覆盖的起始地址是start,并且子网的结束地址不超过end。

这可能需要计算该子网的网络地址,然后找到最大的mask,使得该子网的广播地址不超过end。

例如,子网的网络地址是start & mask,而广播地址是network | (~mask & 0xFFFFFFFFL)。所以需要确保广播地址 <= end。

但这个过程可能比较复杂。因此,可能需要另一种方法:

对于当前的start,尝试找到最大的mask,使得该mask对应的块大小(即2^(32 - mask))使得start + block_size -1 <= end.

同时,mask必须满足start的二进制中,后(32 - mask)位必须全为0,否则该子网的起始地址不是start,而是更小的地址。

例如,如果start是192.168.0.5,而mask是24,那么子网的起始地址是192.168.0.0,而start不在该子网的起始地址,因此无法使用mask为24。因此,必须找到最大的mask,使得start是该子网的起始地址,即start的后(32 - mask)位必须是0。

因此,正确的步骤应该是:

计算从start开始,最大的后缀0的位数。这可以通过计算start的最低位1的位置,比如start的二进制中有n个后缀0,那么最大的mask是32 - n。此时,块大小是2^n。例如,start是192.168.0.0(即…00000000),那么n是8,mask是24,块大小是256。

但如果start是192.168.0.128,二进制是…10000000,那么后缀0的位数是7,所以mask是25,块大小是128。此时,子网是192.168.0.128/25,广播地址是192.168.0.255。

但是,如果end是192.168.0.255,那么这个块可以包含。但是如果end是更小的值,比如192.168.0.200,那么需要调整mask,使得块大小不超过end - start +1。

因此,mask的确定应该是:
mask = 32 - Math.min(lowBit, log2(remainingSize))

其中,lowBit是start的最低位1的位置,remainingSize是end - start +1。

或者,另一种方法是:
mask = 32 - Math.min(lowBit, 32 - (int)(Math.log(remainingSize)/Math.log(2)))

但是,计算lowBit的方式需要正确。例如,对于start的二进制,找到最右边1的位置。比如,start的二进制是…0001000,那么lowBit是3(从0开始计数),即2^3=8。所以块的大小是8,mask是32 -3 =29。

3.2 如何计算lowBit呢?

可以用位运算来找最右边的1的位置。例如,对于long类型的start,取值为start & -start,这会得到一个只有最右边1的数,然后计算log2。

例如,start & -start可以得到一个数,其中只有最右边的1被保留。例如,start是5(二进制101),start & -start的结果是1(二进制1),所以log2(1)=0,即lowBit=0。此时,块的大小是1,mask=32-0=32.

如果是start是8(二进制1000),则start & -start=8log2(8)=3,所以lowBit=3,块的大小是8,mask=29.

所以,lowBit = log2(start & -start). 但如何计算log2?可以用位运算,或者循环右移。

在Java中,可以使用Long.numberOfTrailingZeros()方法,这个方法返回的是二进制中最低位1后面的0的个数。例如,对于8(1000),numberOfTrailingZeros是3。此时,lowBit就是这个值,即块的大小是1 << lowBit。

但这个方法是否适用于所有情况?

假设start是0,那么start & -start是0,这可能有问题。但start在这里是介于起始和结束之间的,所以可能不会为0?

但起始IP可能为0,例如0.0.0.0到0.0.0.255。此时,lowBit是8,块的大小是256,mask是24.

所以,lowBit的计算应该是:

long diff = start & -start;

如果diff ==0,那么lowBit=32?或者如何处理?

或者,另一种方式,使用Long.numberOfTrailingZeros(startIpLong)来获取最低位1后面的0的个数。例如,对于startIpLong=8(二进制1000),这个方法会返回3,因为末尾有3个0。此时,块的大小是1 << 3 =8.

所以,lowBit = Long.numberOfTrailingZeros(startIpLong | 0xFFFFFFFF00000000L)? 不太确定,可能需要仔细测试。

或者,对于IPv4的32位,startIpLong是32位的数值,存储在long类型的高32位或者低32位?可能要看转换方式是否正确。例如,前面的转换函数是否正确地将IP转换为长整数,比如将第一个八位移到高位。

例如,IP地址a.b.c.d转换为长整数的方式是:a<<24 | b<<16 | c<<8 | d。这样,IPv4的32位被放在long的高32位还是低32位?

假设转换正确的话,那么对于192.168.0.5,转换为长整数是 (192 <<24) | (168<<16) | (0<<8) |5 = 3232235525。二进制表示为:

11000000 10101000 00000000 00000101

这里,最低位的1是在第0位(最后一个字节的5是00000101,最低位是1)。所以,Long.numberOfTrailingZeros(3232235525)应该是0,因为最低位是1,后面没有0。所以块的大小是1,mask是32-0=32。

这样,每个块的大小是根据start的最低位1的位置决定的,但也要确保块的大小不超过剩余的地址空间(即end - start +1)。

3.3 综合起来,步骤如下

  1. startIpendIp转换为长整数s和e,确保s <=e。

  2. 初始化结果列表。

  3. s <= e时,循环:

a. 计算当前s的最低位1的位置,得到lowBit = Long.numberOfTrailingZeros(s).

b. 可能的块大小maxSize = 1 << lowBit.

c. 但maxSize不能超过e - s +1,所以实际块大小是Math.min(maxSize, e -s +1).

d. 这时候,需要找到最大的mask,使得块大小为actualSize。因为actualSize必须是2的幂,所以mask = 32 - log2(actualSize).

e. 所以,mask = 32 - (int)(Math.log(actualSize)/Math.log(2)).

f. 将CIDR块添加到结果列表:IP/mask.

g. s += actualSize.

这样,每次循环处理一个块,直到覆盖整个范围。

例如,start=192.168.0.5end=192.168.0.7:

s=3232235525 (0xC0A80005), e=3232235527.

第一次循环:

lowBit = trailing zeros of s=5(二进制101),即0位,所以maxSize=1<<0=1.

actualSize=min(1, 7-5+1=3) →1.

mask=32 -0=32.

CIDR块是192.168.0.5/32.

s +=1 →6.

第二次循环:

s=6,二进制00000110,trailing zeros是1(因为最后两位是 10 →最低位是0?)

不,Long.numberOfTrailingZeros(6)是1,因为6的二进制是110,末尾有一个0。

所以,lowBit=1 →maxSize=2.

actualSize=min(2, 7-6+1=2) →2.

mask=32 -1=31.

CIDR块是192.168.0.6/31,覆盖6和7.

s +=2 →8,超出e=7,循环结束。

所以,结果是两个CIDR块:/32和/31.

这似乎正确。

另一个例子,start=192.168.0.0end=192.168.3.255

s=3232235520, e=3232238079.

第一次循环:

lowBit=Long.numberOfTrailingZeros(s) →s=0xC0A80000 →二进制是11000000 10101000 00000000 00000000 →末尾有16个零?或者可能要看具体的转换方式。

如果IP地址转换为长整数的方式是每个八位作为高位,例如:

对于192.168.0.0,转换为:

192 <<24 | 168 <<16 | 0 <<8 |0 →即3232235520,二进制是11000000 10101000 00000000 00000000

Long.numberOfTrailingZeros(3232235520)→计算末尾的0的个数。3232235520的二进制是:

11000000 10101000 00000000 00000000 →最后的16位都是0,所以 trailing zeros是16.

所以,lowBit=16 →maxSize=1<<16=65536.

但实际剩余的大小是 e -s +1 = 3232238079 -3232235520 +1=2560.

所以actualSize=min(65536, 2560)=2560.

但2560不是2的幂次方。这时候需要调整,找到最大的2的幂次方,不超过2560.

所以,这里可能我的之前的思路有问题,即块的大小必须是2的幂次方,而实际剩余的大小可能不是,因此需要取最大可能的块,其大小是2的幂次方,并且不超过剩余的大小。

这时候,应该怎么做?

例如,在步骤c中,maxSize由lowBit决定,即可能的块大小是2^lowBit,但可能这个大小超过了剩余的大小,因此需要将actualSize设置为最大的2的幂次方,不超过剩余的大小。

或者,另一种方式,actualSize是最大的2的幂次方,使得actualSize <= remainingSize,并且actualSize <= maxSize(即由lowBit决定的块大小)。

例如,remainingSize是2560,那么最大的2的幂次方是2048(2^11=2048),而maxSize由lowBit=16是65536,所以actualSize=2048.

此时,mask=32 -11=21.

这样,CIDR块是192.168.0.0/21?但是这样的话,该块的范围是192.168.0.0到192.168.7.255,但用户的结束IP是192.168.3.255,因此这样会超出范围。这说明我的逻辑有问题。

哦,这说明原来的算法可能存在问题。可能需要重新考虑如何确定块的大小。

正确的逻辑应该是,找到最大的mask,使得该子网的起始地址是s,并且该子网的结束地址 <=e.

也就是说,子网的掩码决定了块的大小是2^(32 - mask),所以我们需要找到最大的mask(即最小的块大小),使得 s + (2^(32 - mask)) -1 <=e.

同时,该mask必须满足s的后(32 - mask)位是0,也就是s是该子网的起始地址。

例如,对于s=192.168.0.0e=192.168.3.255.

最大的mask是22,因为块大小是2(32-22)=210=1024,而s+1024-1=192.168.3.255。这正是需要的。

所以,正确的CIDR192.168.0.0/22.

因此,在这种情况下,算法应该返回一个CIDR块,而不是多个。那问题出在哪里?

块的大小应该同时满足:

  1. 该块的大小是2的幂次方,并且该块的起始地址是s。

  2. 该块的结束地址不超过e.

所以,块的大小应该是最大的可能的2的幂次方,满足 s + size -1 <=e,并且 size是最大的可能值,并且s mod size ==0.

3.4 如何计算这样的size?

算法步骤:

  • 计算remaining = e - s +1.

  • 初始size为最大的可能,即最大的2的幂次方 <= remaining.

  • 同时,size必须满足 s & (size -1) ==0 (即s是该size的倍数)。

如果这两个条件满足,则size可以是该值,否则,需要将size减半,直到满足条件。

例如,对于s=192.168.0.0,e=192.168.3.255,remaining=1024.

最大的size是1024,且s mod 1024 ==0,所以可以使用size=1024,mask=22.

所以,正确的CIDR是/22.

那如何将此逻辑转化为代码?

实现步骤:

  1. 计算remaining = e -s +1.

  2. 计算maxMask = 32 - (int)(Math.log(remaining)/Math.log(2))→但这可能不正确,因为remaining可能不是2的幂次方。

或者,找到最大的mask,使得 2^(32 - mask) <= remaining,并且 s & (2^(32 -mask) -1) ==0.

正确的算法应该是:

循环处理,每次找到最大的size(2的幂次方),使得:

size <= remaining

并且 s + size -1 <=e

并且 s & (size -1) ==0 (即s是size的整数倍)

然后,mask=32 - log2(size).

然后,将CIDR块加入列表,s += size,remaining = e -s +1.

重复直到remaining=0.

现在,如何在代码中实现这一点?

步骤:

while s <=e:remaining = e -s +1;maxSize = 1 << 30; // 初始为较大的数,比如2^30size = 0;for (int shift = 0; shift <=32; shift++) {currentSize = 1 << shift;if (currentSize > remaining) break;if ((s & (currentSize -1)) ==0) {size = currentSize;}
}// 此时size是最大的满足条件的sizemask = 32 - log2(size) →即mask=32 - shift.add cidr: ipToString(s) + "/" + masks += size

但这样可能需要一个循环来找到最大的size。

或者,可以使用位运算来找到最大的可能size:

size = remaining;

然后,找到最大的size,是2的幂次方,且满足 size <= remaining,并且 (s & (size -1)) ==0.

例如:

size = Integer.highestOneBit(remaining); // 最大的2的幂次方 <= remaining
while (size >0) {if ((s & (size -1)) ==0) {break;}size >>=1;
}

这样,size被调整为最大的满足条件的size.

例如,计算size的方法:

long size = Long.highestOneBit(remaining);

然后,循环检查 (s & (size -1)) ==0,否则将size减半,直到找到满足条件的size.

代码可能如下:

long remaining = e - s +1;
long size = Long.highestOneBit(remaining);
while (size >0) {if ((s & (size -1)) ==0) {break;}size >>=1;
}if (size ==0) {size =1;
}

然后,mask=32 - log2(size). 但如何计算log2(size)?

可以用Long.numberOfLeadingZeros来得到。例如,对于size=1024,二进制是10000000000,共11位,所以numberOfLeadingZeros64-11=53。但Long.numberOfLeadingZeros(1024)返回的是64 - 11 =53?这可能需要调整。

或者,计算log2(size)可以使用位运算:

int logSize = 63 - Long.numberOfLeadingZeros(size);

例如,size=1024 →63 - 53(numberOfLeadingZeros(1024))10 →log2(1024)=10.

所以,mask=32 - logSize.

这样,例如size=1024 →mask=22.

所以,综合以上分析,Java代码的大致结构可以是:

public static List<String> ipRangeToCidrs(String startIp, String endIp) {// 转换startIp和endIp为long s和eList<String> cidrs = new ArrayList<>();long s = ipToLong(startIp);long e = ipToLong(endIp);while (s <= e) {// 计算remaininglong remaining = e - s +1;// 计算最大的sizelong size = 1;int shift =0;// 找到最大的size=2^shift <= remaining,并且s & (size-1) ==0for (shift =0; (1L << (shift+1)) <= remaining; shift++) {if ((s & ( (1L << (shift+1)) -1 )) !=0) {break;}}size = 1L << shift;// 计算maskint mask = 32 - shift;// 添加CIDRcidrs.add(longToIp(s) + "/" + mask);// 更新ss += size;}return cidrs;
}

四、ipToLong函数:

public static long ipToLong(String ip) {String[] octets = ip.split("\\.");if (octets.length !=4) {throw new IllegalArgumentException("Invalid IP address");}long result =0;for (int i=0; i<4; i++) {int octet = Integer.parseInt(octets[i]);if (octet <0 || octet >255) {throw new IllegalArgumentException("Invalid IP octet");}result <<=8;result |= octet;}return result;
}

这样,对于192.168.0.5,结果是 (192 <<24) | (168 <<16) | (0 <<8) |5 = 3232235525L.

五、longToIp函数:

public static String longToIp(long ip) {return String.format("%d.%d.%d.%d",(ip >>24) & 0xFF,(ip >>16) & 0xFF,(ip >>8) & 0xFF,ip & 0xFF);
}

这样,3232235525L会被正确转换为192.168.0.5.

六、编写主要的循环部分

public static List<String> ipRangeToCidr(String startIp, String endIp) {long start = ipToLong(startIp);long end = ipToLong(endIp);List<String> result = new ArrayList<>();if (start > end) {throw new IllegalArgumentException("Start IP must be <= End IP");}while (start <= end) {// 计算当前start的最低位1的位置,确定最大的可能的masklong maxSize = 1;int shift =0;// 找到最大的shift,使得 (1 << shift) <= remaining,并且 (start & ((1 << shift) -1)) ==0long remaining = end - start +1;// 初始shift为0,逐步增加,直到无法满足条件for (shift =0; (1L << (shift+1)) <= remaining; shift++) {// 检查如果增加shift后,是否start的后面的位都是0long mask = (1L << (shift +1)) -1;if ((start & mask) !=0) {break;}}long currentSize = 1L << shift;int mask = 32 - shift;result.add(longToIp(start) + "/" + mask);start += currentSize;}return result;
}

综上所述,最终的Java函数应该包括:

  • ipToLonglongToIp的转换函数。

  • 主函数处理循环,找到每个可能的CIDR块。

  • 正确计算size和mask.

七、完整代码实现

以下是完整的Java 代码,并处理可能的异常和边界情况,可以将起始 IP 和结束 IP 转换为最精确的 CIDR 块列表。该方法通过位运算动态计算每个 CIDR 块的范围,确保覆盖所有 IP 且不重叠:

import java.util.ArrayList;
import java.util.List;public class CidrCalculator {public static List<String> convertIpRangeToCidr(String startIp, String endIp) {long start = ipToLong(startIp);long end = ipToLong(endIp);List<String> cidrList = new ArrayList<>();if (start > end) {throw new IllegalArgumentException("起始IP不能大于结束IP");}while (start <= end) {// 计算当前起始IP的最低位有效位(决定CIDR块的大小)int suffixZeros = Long.numberOfTrailingZeros(start);long maxSize = 1L << suffixZeros;  // 可能的块大小(2^suffixZeros)long remaining = end - start + 1;  // 剩余需要覆盖的IP数量// 实际块大小不能超过剩余范围long actualSize = Math.min(maxSize, remaining);// 计算CIDR的掩码位数(32 - log2(actualSize))int mask = 32 - (int) (Math.log(actualSize) / Math.log(2));// 将CIDR块加入列表cidrList.add(longToIp(start) + "/" + mask);// 移动到下一个块的起始IPstart += actualSize;}return cidrList;}// 将IP字符串转换为32位整数(用long存储)private static long ipToLong(String ip) {String[] octets = ip.split("\\.");if (octets.length != 4) {throw new IllegalArgumentException("无效的IP地址格式");}long result = 0;for (int i = 0; i < 4; i++) {int octet = Integer.parseInt(octets[i]);if (octet < 0 || octet > 255) {throw new IllegalArgumentException("IP八位组超出范围");}result = (result << 8) | octet;}return result;}// 将32位整数转换为IP字符串private static String longToIp(long ip) {return ((ip >> 24) & 0xFF) + "." +((ip >> 16) & 0xFF) + "." +((ip >> 8) & 0xFF) + "." +(ip & 0xFF);}// 测试用例public static void main(String[] args) {// 示例1:单个IP(/32)test("192.168.0.5", "192.168.0.5");  // ["192.168.0.5/32"]// 示例2:连续两个IP(/31)test("192.168.0.6", "192.168.0.7");  // ["192.168.0.6/31"]// 示例3:一个完整的C类子网(/24)test("192.168.0.0", "192.168.0.255");  // ["192.168.0.0/24"]// 示例4:跨子网的IP范围test("192.168.0.5", "192.168.0.7");  // ["192.168.0.5/32", "192.168.0.6/31"]}private static void test(String startIp, String endIp) {List<String> cidrs = convertIpRangeToCidr(startIp, endIp);System.out.println("IP范围: " + startIp + " - " + endIp);System.out.println("CIDR块: " + String.join(", ", cidrs) + "\n");}
}

7.1 代码说明

  1. 核心逻辑

    • 位运算计算:通过 Long.numberOfTrailingZeros 找到起始 IP 的最低位有效位,确定最大可能的 CIDR 块大小。
    • 动态调整块大小:根据剩余 IP 范围 (remaining) 和起始 IP 的对齐要求,动态调整实际 CIDR 块大小。
  2. 关键方法

    • ipToLong():将 IP 字符串转换为 32 位整数(用 long 存储,避免符号问题)。
    • longToIp():将整数转换回点分十进制格式。
    • convertIpRangeToCidr():主方法,遍历 IP 范围生成 CIDR 列表。
  3. 测试案例

    • 覆盖单 IP、连续 IP、完整子网和跨子网范围,验证不同场景的准确性。

7.2 执行示例

输入:

convertIpRangeToCidr("192.168.0.6", "192.168.0.7");

输出:

CIDR块: 192.168.0.6/31

输入:

convertIpRangeToCidr("192.168.0.5", "192.168.0.7");

输出:

CIDR块: 192.168.0.5/32, 192.168.0.6/31

相关文章:

IP段转CIDR:原理Java实现

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

翻译: 深入分析LLMs like ChatGPT 一

大家好&#xff0c;我想做这个视频已经有一段时间了。这是一个全面但面向普通观众的介绍&#xff0c;介绍像ChatGPT这样的大型语言模型。我希望通过这个视频让大家对这种工具的工作原理有一些概念性的理解。 首先&#xff0c;我们来谈谈你在这个文本框里输入内容并点击回车后背…...

springboot之HTML与图片生成

背景 后台需要根据字段动态生成HTML&#xff0c;并生成图片&#xff0c;发送邮件到给定邮箱 依赖 <!-- freemarker模板引擎--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifa…...

数据结构(初阶)(三)----单链表

单链表 概念 概念&#xff1a;链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 结点 与顺序表不同的是&#xff0c;链表的结构类似于带车头的火车车厢&#xff0c;&#xff0c;链表的每个车厢都是独立…...

ChatGPT与DeepSeek:AI语言模型的巅峰对决

目录 引言 一、ChatGPT 与 DeepSeek 简介 &#xff08;一&#xff09;ChatGPT &#xff08;二&#xff09;DeepSeek 二、技术原理剖析 &#xff08;一&#xff09;ChatGPT 技术原理 &#xff08;二&#xff09;DeepSeek 技术原理 &#xff08;三&#xff09;技术原理对比…...

DaoCloud 亮相 2025 GDC丨开源赋能 AI 更多可能

2025 年 2 月 21 日至 23 日&#xff0c;上海徐汇西岸&#xff0c;2025 全球开发者先锋大会以 “模塑全球&#xff0c;无限可能” 的主题&#xff0c;围绕云计算、机器人、元宇宙等多元领域&#xff0c;探讨前沿技术创新、应用场景拓展和产业生态赋能&#xff0c;各类专业论坛、…...

人工智能之数学基础:线性代数中矩阵的运算

本文重点 矩阵的运算在解决线性方程组、描述线性变换等方面发挥着至关重要的作用。通过对矩阵进行各种运算,可以简化问题、揭示问题的本质特征。在实际应用中,我们可以利用矩阵运算来处理图像变换、数据分析、电路网络等问题。深入理解和掌握矩阵的运算,对于学习线性代数以…...

(上)基于机器学习的图像识别——遥感图像分类(LeNet-5;AlexNet;VGGNet;GoogLeNet;ResNet)

遥感图像识别&#xff1a; 专业词汇&#xff1a; kernel&#xff1a;卷积 目录 遥感图像分类 1.1 LeNet-5 视频来源&#xff1a; 任务&#xff1a;使用什么网络实现遥感图像的分类 LeNet-5结构&#xff1a; 遥感图像分类 1.2 AlexNet&#xff08;冠军&#xff09; 视频…...

数据集笔记:NUSMods API

1 介绍 NUSMods API 包含用于渲染 NUSMods 的数据。这些数据包括新加坡国立大学&#xff08;NUS&#xff09;提供的课程以及课程表的信息&#xff0c;还包括上课地点的详细信息。 可以使用并实验这些数据&#xff0c;它们是从教务处提供的官方 API 中提取的。 该 API 由静态的…...

HTML元素,标签到底指的哪块部分?单双标签何时使用?

1. 标签&#xff08;Tag&#xff09; vs 元素&#xff08;Element&#xff09; 标签&#xff08;Tag&#xff09; 标签是 HTML 中用于定义元素的符号&#xff0c;用尖括号 < > 包裹。例如 <img> 是标签。元素&#xff08;Element&#xff09; 元素是由 标签 内容…...

基于ai技术的视频生成工具

一、通用型AI视频生成工具 腾讯智影 特点&#xff1a;支持数字人播报、文字转视频&#xff0c;提供免费模板和素材库&#xff0c;登录即送5分钟免费时长&#xff0c;每日签到可兑换额外额度。 限制&#xff1a;免费版分辨率较低&#xff0c;部分高级功能需付费。 LunaAI.vid…...

【Java 后端】Restful API 接口

Restful API 接口 REST&#xff1a;Representational State Transfer&#xff0c;表现层&#xff08;前端的视图页面和后端的控制层&#xff09;资源状态转移。 一种软件架构的风格&#xff08;格式&#xff09; RESTful 是目前最流行的互联网软件架构&#xff0c;如果一个架…...

Matlab地图绘制教程第2期—水陆填充图

上一期分享了海岸线图的绘制方法&#xff1a; 本着由浅入深的理念&#xff0c;本期再来分享一下水陆填充图的绘制方法。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;Matlab地图绘制教程系列&#xff0c;旨在降低大家使用Matlab进行地图类科研绘图的门槛&#xff0c;…...

企业知识库搭建:14款开源与免费系统选择

本文介绍了以下14 款知识库管理系统&#xff1a;1.Worktile&#xff1b;2.PingCode&#xff1b;3.石墨文档&#xff1b; 4. 语雀&#xff1b; 5. 有道云笔记&#xff1b; 6. Bitrix24&#xff1b; 7. Logseq等。 在如今的数字化时代&#xff0c;企业和团队面临着越来越多的信息…...

【Linux系统】—— 冯诺依曼体系结构与操作系统初理解

【Linux系统】—— 冯诺依曼体系结构与操作系统初理解 1 冯诺依曼体系结构1.1 基本概念理解1.2 CPU只和内存打交道1.3 为什么冯诺依曼是这种结构1.4 理解数据流动 2 操作系统2.1 什么是操作系统2.2 设计OS的目的2.3 操作系统小知识点2.4 如何理解"管理"2.5 系统调用和…...

Android内存优化指南:从数据结构到5R法则的全面策略

目录 一、APP 内存限制 二、内存的三大问题 2.1、内存抖动(Memory Churn) 2.1.1 频繁创建短生命周期对象 2.1.2 系统API或第三方库的不合理使用 2.1.3 Handler使用不当 2.2、内存泄漏(Memory Leak) 2.2.1 静态变量持有Activity或Context引用 2.2.2 未取消的回调或…...

机器学习:线性回归,梯度下降,多元线性回归

线性回归模型 (Linear Regression Model) 梯度下降算法 (Gradient Descent Algorithm) 的数学公式 多元线性回归&#xff08;Multiple Linear Regression&#xff09;...

Linux上用C++和GCC开发程序实现两个不同MySQL实例下单个Schema稳定高效的数据迁移到其它MySQL实例

设计一个在Linux上运行的GCC C程序&#xff0c;同时连接三个不同的MySQL实例&#xff0c;其中两个实例中分别有两个Schema的表结构分别与第三实例中两个Schema个结构完全相同&#xff0c;同时复制两个实例中两个Schema里的所有表的数据到第三个实例中两个Schema里&#xff0c;使…...

RabbitMQ系列(一)架构解析

RabbitMQ 架构解析 RabbitMQ 是一个基于 AMQP 协议的开源消息中间件&#xff0c;其核心架构通过多组件协作实现高效、可靠的消息传递。以下是其核心组件与协作流程的详细说明&#xff1a; 一、核心组件与功能 Broker&#xff08;消息代理服务器&#xff09; RabbitMQ 服务端核…...

XSL 语言:XML 样式表的语言基础与应用

XSL 语言:XML 样式表的语言基础与应用 引言 XSL(Extensible Stylesheet Language)是一种专门用于XML文档样式的语言,它允许用户定义XML文档的格式、布局和外观。XSL是XML技术家族中的重要组成部分,与XML和XPATH等语言共同构成了处理和格式化XML文档的强大工具集。本文将…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...