普通视图

发现新文章,点击刷新页面。
昨天以前首页

👶 小孩报数问题:当熊孩子遇上“约瑟夫环

2025年8月15日 22:51

👶 小孩报数问题:当熊孩子遇上“约瑟夫环”

哈喽,各位掘友们!你有没有想过,一群天真无邪的小朋友围成一圈,玩着报数游戏,结果报到特定数字的就要“出局”?这听起来是不是有点“残忍”?😂 但别担心,我们今天不是来搞“淘汰赛”的,而是要用代码来揭秘这个经典算法问题——约瑟夫环问题(Josephus Problem)的一个变种!

准备好了吗?系好安全带,咱们这就出发,一起看看这群“熊孩子”是怎么玩转算法的!🚀

❓ 问题来了:谁是最后的幸存者?

场景描述

想象一下,有30个小朋友,编号从1到30,手拉手围成一个大大的圆圈。他们要开始报数了,从1号小朋友开始,依次报1、2、3……报到3的小朋友就要“出局”,然后下一个小朋友(也就是原来报4的小朋友)重新从1开始报数。如此循环往复,直到只剩下最后一个小朋友。那么问题来了,这位“天选之子”的编号是多少呢?

是不是听起来有点像“鱿鱼游戏”?别怕,我们只是用代码模拟,没有真的小朋友会受伤!😉

🔧 揭秘算法:代码是这样玩的!

为了解决这个问题,我们请出了一段神奇的JavaScript代码。别看它其貌不扬,里面可是藏着解决问题的“大智慧”!

function childNum(num, count){
    let allPlayer = [];
    for(let i = 0; i < num; i++){
        allPlayer[i] = i + 1;
    }

    let exitCount = 0; // 离开人数
    let counter = 0;   // 记录报数
    let curIndex = 0;  // 当前下标

    while(exitCount < num - 1){
        if(allPlayer[curIndex] !== 0) counter++;

        if(counter === count){
            allPlayer[curIndex] = 0;
            counter = 0;
            exitCount++;
        }
        curIndex++;
        if(curIndex === num){
            curIndex = 0
        }
    }

    for(let i = 0; i < num; i++){
        if(allPlayer[i] !== 0){
            return allPlayer[i]
        }
    }
}

childNum(30, 3);

💡 代码解读:一步步拆解“报数”过程

1. 📝 初始化:小朋友们,各就各位!

首先,childNum(num, count) 函数接收两个参数:num 代表小朋友的总人数(这里是30),count 代表报到几就“出局”(这里是3)。

    let allPlayer = [];
    for(let i = 0; i < num; i++){
        allPlayer[i] = i + 1;
    }

这段代码创建了一个数组 allPlayer,用来模拟围成一圈的小朋友。数组的每个元素就是小朋友的编号。比如,allPlayer[0] 就是1号小朋友,allPlayer[29] 就是30号小朋友。我们用 0 来表示已经“出局”的小朋友。

接着,我们有三个重要的“计数器”:

    let exitCount = 0; // 离开人数:记录已经有多少小朋友“出局”了
    let counter = 0;   // 记录报数:当前小朋友报的数(1、2、3...)
    let curIndex = 0;  // 当前下标:当前正在报数的小朋友在数组中的位置
2. 🔄 循环报数:谁是下一个“幸运儿”?

核心逻辑都在这个 while 循环里。它会一直运行,直到只剩下最后一个小朋友(exitCount < num - 1)。

    while(exitCount < num - 1){
        if(allPlayer[curIndex] !== 0) counter++;

        if(counter === count){
            allPlayer[curIndex] = 0;
            counter = 0;
            exitCount++;
        }
        curIndex++;
        if(curIndex === num){
            curIndex = 0
        }
    }
  • if(allPlayer[curIndex] !== 0) counter++;

    • 这行代码是关键!它检查当前小朋友是否还在圈内(即 allPlayer[curIndex] 不为0)。如果还在,counter 就加1,表示他报了一个数。
    • 如果小朋友已经“出局”了(allPlayer[curIndex] 是0),那么他就不会报数,counter 也不会增加。这很合理,毕竟“人都没了”,还怎么报数呢?😂
  • if(counter === count){ ... }

    • counter 等于我们设定的 count 值(这里是3)时,说明当前小朋友报到了“出局”的数字。
    • allPlayer[curIndex] = 0;:这位小朋友“光荣出局”,我们把他的编号设为0。
    • counter = 0;:报数重新从1开始,所以 counter 重置为0。
    • exitCount++;:离开人数加1,记录又有一个小朋友“出局”了。
  • curIndex++;if(curIndex === num){ curIndex = 0 }

    • 这两行代码负责让报数的小朋友“轮流上岗”。curIndex 每次循环都会加1,指向下一个小朋友。
    • 如果 curIndex 到了数组的末尾(num),说明已经绕了一圈,需要回到数组的开头(0),形成一个完美的“环”!🔄
3. 🏆 寻找幸存者:谁是最后的赢家?

while 循环结束时,意味着只剩下最后一个小朋友了。那么,怎么找到他呢?

    for(let i = 0; i < num; i++){
        if(allPlayer[i] !== 0){
            return allPlayer[i]
        }
    }

这段代码很简单粗暴:遍历 allPlayer 数组,找到那个唯一一个不为0的元素,它就是我们苦苦寻找的“天选之子”的编号!🎉

🧪 运行一下:答案揭晓!

最后,我们调用 childNum(30, 3) 来运行这个游戏。根据代码逻辑,最终会返回最后一个幸存者的编号。

那么,30个小朋友,报数到3出局,最后剩下的是几号小朋友呢?

答案是29,快去试试吧!

总结与思考

算法小结

这个“小孩报数问题”其实是经典的约瑟夫环问题的一种模拟解法。它的核心思想是:

  1. 模拟过程:用数组模拟环形结构,用特定值(0)标记出局者。
  2. 循环计数:通过 counter 变量实现报数功能,遇到出局者跳过。
  3. 环形遍历:通过 curIndex 的重置实现循环报数。

这种模拟方法虽然直观易懂,但当人数非常多时,效率可能会降低。对于大规模的约瑟夫环问题,通常会有更高效的数学解法(例如通过递推公式)。但对于面试或日常小问题,这种模拟解法已经足够清晰和优雅了!

💖 掘友们,你们怎么看?

你有没有遇到过类似的场景题?或者你有什么更风趣幽默的解释方式?欢迎在评论区留言,一起交流学习!

别忘了点赞👍、收藏⭐、转发↗️,让更多掘友看到这篇“不正经”的算法科普文!我们下期再见!👋

大规模建筑自动贴图+单体化效果,cesium脚本

作者 波泼
2025年8月15日 16:16

模型单体化效果是指,点击模型中的某个楼栋,弹出楼栋信息或楼栋扫光高亮。

常见自制模型单体化方法

这是一个常见的高频问题。常用的自制模型单体化有2种方法

1、在模型层面把分组设计好。你如1栋,2栋,3栋等。点击分组模型得到模型分组名,根据分组名请求api得到单体化信息。
2、从自制模型制作gis白模。点击模型后,根据点击点找白模轮廓。

导出的的贴图模型如何单体化?

用户说使用Geobuilding将建筑白模导出成贴图模型。模型有了,白模也有了。领导说要点击建筑物出信息,如何实现单体化交互效果呢?

现在的情形和上面第2种方法是有不同的。

一个是模型gltf->白模geojson. 这里的geojson一定能包含住模型 (上面说的第2种)
一个是白模->模型gltf,这里的模型不一定和白模重合。

为什么不会重合?因为模型内部是局部坐标体系,不是经纬度坐标。 那么如何实现点击模型中某个楼栋,实现单体化交互效果。不会再基于模型二次生产白模geojson吧?当然不用

峰回路转,cesium端的实践

cesium端点击模型后,有世界坐标。根据模型矩阵可转换为模型内部坐标。根据原始模型构建算法,得到点击的相对经纬度。然后拿着经纬度去找轮廓,最后弹出建筑信息或高亮。

该方法集成在了导出的demo html文件中,可作为js插件直接使用。 js插件包含模型全量加载、动态加载、模型扫光、点击单体化。支持cesium低版本和高版本使用。 点击事件代码如下:

const handler = new Cesium.ScreenSpaceEventHandler(_this.viewer.scene.canvas);
            handler.setInputAction(async function(click) {
                const pickedObject = _this.viewer.scene.pick(click.position);
                if(Cesium.defined(pickedObject) && pickedObject['id']&&pickedObject['id']['geojson']){
                    //点击了单体化围墙
                    alert(JSON.stringify(pickedObject['id']['geojson']))
                }else if (Cesium.defined(pickedObject) && pickedObject.primitive.userData['pick2']) {
                    const worldPoint = _this.viewer.scene.pickPosition(click.position);
                    if (Cesium.defined(worldPoint)) {

                        //1、获取模型点击的经纬度和高度。
                        var modellnglat = _this.gltfInstance.GetClickLnglat(_this.viewer, worldPoint, pickedObject)
                        /*
                            2、查询点所在的轮廓。
                            !!!演示文件通过前端来查询点在轮廓内。!!!实际应用中需通过后端查询,避免GIS数据泄漏!
                            内置属性解释
                            https://i1.hdslb.com/bfs/article/55509bc62ca58bb6a8463819342258cd98081c25.png@1192w.avif
                            如果一个建筑体包含多个gis轮廓数据。在Geobuilding软件内对建筑体的gis数据【选择框】-打组。打组后这些gis数据有相同的属性值groupid
                            根据groupid可找到关联数据
                         */
                        let geojson = await (await fetch('geojson/' + pickedObject.primitive.userData.geojson)).text();
                        let result = geojson.split("\n").map(function(r) {return JSON.parse(r);});
                        var hitgeo;
                        for (let i = 0; i < result.length; i++) {
                            if (turf.booleanPointInPolygon(turf.point([modellnglat.lng, modellnglat.lat]), result[i], {ignoreBoundary: false})){
                                var demheight = result[i].properties.demheight;
                                var clickheight = modellnglat.height - demheight;
                                if(clickheight>= result[i].properties.pfh*result[i].properties.minfloor && clickheight<= result[i].properties.pfh*result[i].properties.minfloor + (result[i].properties.wfh*result[i].properties.floor)){
                                    hitgeo = result[i]
                                    break;
                                }
                            }
                        }
                        if(!hitgeo) {
                            console.log("没有找到轮廓")
                            return;
                        }

                        //原始geojson数据
                        //alert(JSON.stringify(hitgeo))

                        //3、对hitgeo并进行偏移转换(相对于模型)。
                        hitgeo = _this.gltfInstance.TransFeature(hitgeo, pickedObject);

                        //4、将geojson转换成cesium世界坐标,添加单体化高亮围墙。
                        var wallpos = turf.buffer(hitgeo, 1, {
                            units: "meters"
                        }).geometry.coordinates[0].map(function(z) {
                            return Cesium.Cartesian3.fromDegrees(z[0], z[1], hitgeo.properties.gltfbheight)
                        })

                        if (wall) wall.remove();
                        wall = new WallObject.FlowWall(_this.viewer,wallpos,{
                                copyright:'geobuilding',
                                wallHeight: hitgeo.properties.wfh*hitgeo.properties.floor,
                                wallColor: Cesium.Color.fromCssColorString('rgba(0, 255, 26, 0.3)'),
                                duration: 1000,
                                materialType: 3,
                            }
                        );
                        wall.flowWallEntity.geojson = hitgeo;
                    }else {
                        console.log('无法获取点击点的世界坐标');
                    }
                }else {
                    console.log('未点击到带有 pick2 的模型');
                }
            }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

下面操作一遍看下效果。

首先选择导出模型

选择批量贴图方案,这里选择 实景风贴图

导出后有demo文件。模型信息.txt中本机浏览地址。

打开导出的demo页面,点击场景中某个建筑,显示高亮。

支持更多场景

基于地形的建筑白模自动化贴图 + 单体化

基于分层的单体化点击操作

点击出建筑楼栋信息

百度智能云x中科大脑:「城市智能体」如何让城市更会思考

作者 百度Geek说
2025年8月14日 16:38
近日,2025中关村论坛系列活动——中关村人工智能与未来城市论坛在中关村国家自主创新示范区展示中心举办。论坛上,发布了应用范式创新升级成果、智能体产品、可信数据空间成果等。 中科大脑联合百度智能云等伙
❌
❌