数学余数,一些新的感悟。

1.同余公式:

记得看书的时候看到一个定理:

定理:A-B 能被 N 整除,那么A 与 B 模 N 同余。记作 A ≡ B (mod N);大概意思是A (mod N) = B (mod N)。

1.1迫不及待的证明一下:

(A - B) % N = 0
那么我们可以假设 (A - B) / N = X 
那么 A - B = NX
A  = NX + B
    = (NX + B) / N 
    =  NX/N + B/N
那么根据公式我们可以得出 A / N 的余数为 B/N的余数

1.2 那么我反过来假设假设 A 与 B 模 N 同余,那么(A - B)一定可以被N整除。

证明:

A 与 B mod N 同余
那么我们可以得到:A 除以 N 商 y 余 z,B 除以 N 商 x 余 z;y和x 都为整数
那么A = yN + z
同理 B = xN + z
(A-B)/N = (yN + z - xN -z)/N
             = (yN-xN)/N
             = (y-x)N/N
             = (y-x)
y-x等于正数。

1.3 那么我们在进一步去推论假设A mod N 的余数z 与 A mod N 同余

根据上面的定义,我们可以得到 
A  = yN + z
z  = 0* N + z
(A - z)/N = (yN + z -z)/N
               =y
很明显(A -z)是能被 N整除的,那么A 与 z 也是同余。

2.落实到地

楼主是在公司数据部门工作,且我的水平并不高,下面就以我实际工作中的一个列子,所以下面的方法和思路不确定会不会是最优解,只是提供一种新的思路。

任务需求:

从Mysql中拿出符合特征一批用户,具体不知道这批用户有多少个,然后ES中去匹配用户的相关消费行为,但是ES的terms有一个参数个数限制(由于公司并没有引入本数据仓库,和大数据框架,处理的方式都是用代码去拼接),我们假设这个参数的最大个数为limit 下面皆为go伪代码部分。

func queryReader[T any](agrs ...[]T)(io.reader){
    .....
    return ...
}

func QureyFromEs(buf io.reader){
    ....
}

func Query[T any](limit int,idSlice []T){
    i := 0 
    for {
        if i + limit >= len(idSlice) {
            buf := queryReader([idSlice[:]...)
            QueryFromEs(buf)
            break
        }
        buf := queryReader(idSlice[:limit]...)
        QueryFromEs(buf)
        idSlice = idSlice[limit:]
    }
}

咋看一下这段代码其实非常简洁利于阅读了,但是其实有两个问题;

  1. idSlice = idSlice[limit:]这种复制重新赋值的方式内存开销,当数据非常多,几百万上千万是很有可能出现om。(如果我们不采用切片重新赋值的方式,当怎么去防止下标溢出。)再来改一次代码:

func queryReader[T any](agrs ...[]T)(io.reader){
    .....
    return ...
}

func QureyFromEs(buf io.reader){
    ....
}

func Query[T any](limit int,idSlice []T){
    start := 0
    end := i + limit 
    A = len(idSlice)
    for { //这里依旧不能使用end <= A 的情况。大家可以自己去思考
        if end => A && start < A  {
            buf := queryReader(idSpice[:])
            QueryFromEs(buf)
            break
        }
        buf := queryReader()
        QueryFromEs(buf)
        start := end
        end := end + limit
    }
}

实际牺牲了代码的可读性来解决了om的问题。

2.姑且不说这个if到底能不能准确的保证在切片取值的时候下标不溢出,当循环体业务代码的判断非常复杂时,代码的可阅读性就降低,会引入非常多的逻辑判断。且同样的函数引用的代码写了两次

2.1 根据今天的公式进行代码优化

我们根据上面的数学定理。假设len(idSlice) 为A ,limit 为N 那么,我们可以得到A % N = z,这个z就是一个余数。那么这个z就是不能被N整除的。既有 z % N = z,那么A 与 z 模 N就是同余,换句话说就是(A-z) % N = 0;那么我们根据这个定理改写一下代码,我们先去z个元素出来,剩下的元素数量就为A - z ,那么它总能被limit的整除。也就是以后的切片无论怎么取都不会出现下标溢出的情况。

func queryReader[T any](agrs ...[]T)(io.reader){
    .....
    return ...
}

func QureyFromEs(buf io.reader){
    ....
}

func func Query[T any](limit int,idSlice []T){
    A := len(idSlice)
    start := 0
    end := A % limit
    for end <= A { //这里为什么就可以了使用end <= A 
        buf := queryReader(idSpice[start:end])
        QueryFromEs(buf)
        start := end
        end := end + limit
    }
}

优化了一次的代码再也不用去判断下标溢出的问题。同时重新赋值时切片元素过大负值时导致的om问题也到得到解决。当然这个程序还是不够完美,因为没有考虑刚好被整除的情况。也是就是start 和 end 都等于0的情况。但是对于Es查询没有影响,对于其他的存储就需要大家留心一下了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值