程序员老鬼

35岁大厂程序员上岸武汉体制内,收入从百万年薪锐减到十几万后,妻子嫌弃他没上进心。网友:这种老公,离!

刚看到个贴子,说一位35岁大厂程序员,上岸武汉体制内,从百万年薪变成一年十几万,结果老婆嫌他没上进心。底下不少网友直接一句:这种老公,离!

Image

网友喊“离”,说实话有点上头了。婚姻又不是只看工资条,多赚是一种责任,少犯错、活得久,也是另一种责任。

真正的问题是:这事两个人有没提前算清账、谈明白预期?是一起做的选择,还是他一个人拍脑袋?

在我看来,家庭更像合伙开公司:有人负责冲锋,有人负责稳住盘子,可以换赛道,但别瞒着队友。比起一句“离”,我更希望他们能坐下来,把未来十年的钱、健康、孩子,一起摊开聊清楚,这才叫上进。

面试题:每台机器的进程平均运行时间

昨天晚上十一点多,我在公司楼下抽烟,手机一震,是我们组那个小李:“东哥,你说监控系统里面那堆进程日志,有没有简单一点的算⼀下,每台机器平均一个进程跑多久啊?老板要个报表。” 我当时脑子已经半糊涂了,但这事儿其实挺典型的一个小算法题。

你可以想象这样一串数据,每条都长得差不多:

  • machineId:机器编号,比如 1、2、3
  • processId:进程编号
  • startTime:开始时间戳(毫秒)
  • endTime:结束时间戳(毫秒)

也就是一行行类似这样的记录:

机器A,进程p1,从 1000ms 跑到 3000ms
机器A,进程p2,从 2000ms 跑到 2600ms
机器B,进程q1,从 1500ms 跑到 4500ms
...

问题就变成一句话:按机器分组,把这一台机器上所有进程的运行时长取平均值。

跟我之前做数据库压测那种“按库统计平均耗时”的活儿一个味道,只不过这次是按机器来聚合。

这种题要是你一上来就想各种“时间轴呀、排序呀、窗口呀”,基本就是自己吓自己。

最朴素那版思路就两件事:

  1. 每条记录先算出这个进程的运行时长:duration = endTime - startTime
  2. 按 machineId 把这些 duration 累加起来,同时数一下有多少个进程,最后 avg = 总时长 / 进程数量

用人话说,就是: “这一台机器所有进程的用时都加一块儿,然后除一下个数。”

时间复杂度就是 O(n),一条记录只看一眼;空间复杂度大概是 O(m),m 是机器数量,一般也不算多。

小李当时就一句话:“你别跟我说思路,直接丢个方法给我。”那就写个简单点的版本:

import java.util.*;

publicclassMachineAvgRuntime{

// 一条进程记录
publicstaticclassProcessRecord{
privateint machineId;
private String processId;
privatelong startTime; // 毫秒时间戳
privatelong endTime;   // 毫秒时间戳

publicProcessRecord(int machineId, String processId, long startTime, long endTime){
this.machineId = machineId;
this.processId = processId;
this.startTime = startTime;
this.endTime = endTime;
        }

publicintgetMachineId(){
return machineId;
        }

publiclonggetDuration(){
// 简单防御:如果 end 比 start 小,就当成 0
return Math.max(0, endTime - startTime);
        }
    }

// 核心方法:算每台机器的平均进程运行时间
publicstatic Map<Integer, Double> calcAvgRuntimePerMachine(List<ProcessRecord> records){
// key: machineId
// value[0]: 总时长之和,value[1]: 进程数量
        Map<Integer, long[]> statMap = new HashMap<>();

for (ProcessRecord record : records) {
int machineId = record.getMachineId();
long duration = record.getDuration();

long[] stat = statMap.get(machineId);
if (stat == null) {
                stat = newlong[2];
                statMap.put(machineId, stat);
            }
            stat[0] += duration; // 累加总时长
            stat[1] += 1;        // 进程计数 +1
        }

// 把结果转成平均值
        Map<Integer, Double> result = new HashMap<>();
for (Map.Entry<Integer, long[]> entry : statMap.entrySet()) {
int machineId = entry.getKey();
long totalDuration = entry.getValue()[0];
long count = entry.getValue()[1];

if (count == 0) {
                result.put(machineId, 0.0);
            } else {
// 这里转 double,避免整型除法精度丢失
                result.put(machineId, totalDuration * 1.0 / count);
            }
        }

return result;
    }

// 随便写个 main 测一下
publicstaticvoidmain(String[] args){
        List<ProcessRecord> list = new ArrayList<>();
// 机器 1 上两个进程:时长分别 2000 和 600
        list.add(new ProcessRecord(1, "p1", 1000, 3000));
        list.add(new ProcessRecord(1, "p2", 2000, 2600));
// 机器 2 上一个进程:时长 3000
        list.add(new ProcessRecord(2, "q1", 1500, 4500));

        Map<Integer, Double> avg = calcAvgRuntimePerMachine(list);
        System.out.println(avg); // 比如:{1=1300.0, 2=3000.0}
    }
}

这个实现比较无脑,但胜在清楚:

  • 用 Map<Integer, long[]> 做统计容器,不额外建一堆类
  • long[0] 存总时长,long[1] 存数量,最后统一扫一遍算平均值
  • 最外层结果再转成 Map<Integer, Double>,给前端图表、报表用都还行

行了,我这边咖啡也凉了,就先这样,小李那报表后来顺利交差,你要是真线上要用,记得别在 SQL 里一层层嵌套聚合干这个,用程序扫一遍,稳。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

最后给大家分享一份不错的副业资料,点击下方公众号,回复关键字: 副业 领,也可以链接我微信:hls404