瀑布流引发的思考

事件起因:有一天在某篇文章看到这样一道题目

A:你有写过瀑布流吗?

B:我写过等宽瀑布流。实现是当用户拉到底部的一定高度的时候,向后端请求一定数量的图片,然后再插入到页面中。

A:那我问一下,如何让几列图片之间的高度差最小?

B:这个需要后端发来的数据里面有图片的高度,然后我就可以看当前高度最小的是哪里列,将新图片插入那一列,然后再看看新的高度最小的是哪一列。

A:我觉得你没有理解我的问题,我的意思是如何给后端发来的图片排序,让几列图片之间的高度差最小?

B:(想了一段时间)对不起,这个问题我没有思路。

 

转化一下问题,将任意数分成特定的组,使得每组的和尽量相等

那么怎么解决这个问题呢?

记得刚接触编程的时候,有人和我说过写函数就是找规律,找到内部的共同点,我们现在就按这个方法尝试探究一下这个问题

不考虑分成一组的情况,我们直接考虑两组

在两组的情况下,很容易可以想到只要将其中一组尽可能拿到总和的1/2即可

那么这个问题就被转化为了01背包问题

条件:数组中的数就是背包问题的weight值,数组中的数也是背包问题的value值,即二者一样。

问题:背包里装哪些物品,使得其价值之和最接近总价值的一半。

01背包的处理取决于拿与不拿

简单陈述通过动态规划来求解0-1背包问题的思路。

用f(1,2,3,4....i)(j)表示背包为j时能在i个物品中取出的最大价值

用w(i),v(i)表示第i个物品的重量个价值

拿的表达式 f(1,2,3....i-1)(j-w(i))+v(i)

不拿的表达式 f(1,2,3....i-1)(j)

获得状态转移方程f(i)(j)=max(f(i-1)(j-w(i))+v(i),f(i-1)(j))       1,2,3...i省略成i

chooseOrNot=(List, halfNum)=>{
        let len = List.length
        let f = new Array(len)
        f[-1] = new Array(halfNum+1).fill(0) //使f[0-1][j]存在
        let selected = []
        for(let i = 0 ; i < len ; i++){ 
            f[i] = [] //创建二维数组 
            for(let j=0; j<=halfNum; j++){ 
                if( j < List[i] ){ 
                    f[i][j] = f[i-1][j] //物体比背包大
                }else{
                    f[i][j] = Math.max(f[i-1][j], f[i-1][j-List[i]]+List[i])
                }
            }
        }
        let j = halfNum, w = 0
        for(let i=len-1; i>=0; i--){
            if(f[i][j] > f[i-1][j]){
                selected.push(i)
                j = j - List[i]
                w +=  List[i]
            }
        }
        return [f[len-1][halfNum], selected]
    }

运用上述的方法,我们能不能获得将数组均分为三组的最优解呢?

我将之前的例子[2,3,4,5,6]带入得到了 [2,4],[5],[3,9]很显然这并不是最优解。

然后将任意数分成三组,使得每组的和尽量相等这个问题我陷入了两天的思考,搞得我浑身难受

最后我把这个问题抛给了我的组合数学老师

然后老师告诉我这个问题是NP完全的问题  https://en.wikipedia.org/wiki/3-partition_problem

居然一下子碰到了数学的天花板,世界数学七大难题之一。。。


回到'三体问题',贪心算法在数据比较均匀的时候不失为一种解决方法

模仿儿童为游戏选择团队的方式的问题的一种方法是贪婪算法,其以降序迭代数字,将它们中的每一个分配给具有较小总和的任何子集。该方法的运行时间为On log n。当该集合中的数字与其基数大小相同或更小时,该启发式在实践中运行良好,但不能保证产生最佳可能的分区。

也就是我们常写的瀑布流,当然需要排序一下,比较简单直接拓展到n了

threeBody = (array,n) => {
	array.sort((a, b) => {
		return b - a
	})
	let list=[]
	for (let j = 0; j < n; j++) {
		list[j] = []
	}
	let arr=[]
	for (let i = 0; i < array.length; i++) {
		for (let j = 0; j < n; j++) {
			arr[j]=sum(list[j])
		}
		min = Math.min(...arr)
		for (let j = 0; j < n; j++) {
			if(min==arr[j]){
				list[j].push(array[i])
				break
			}
		}
		}
	return(list,arr)
}

既然这样,我们之前抛弃的方法显然也是一种贪心算法,每次尽量取出1/n,感兴趣的,可以去比较一下这几种方法


当然这类问题还有适用于不同情境的算法

例如分治求解法 这里贴一篇论文  http://journal.nudt.edu.cn/publish_article/2004/6/200406024.pdf

 

 

你可能感兴趣的:(瀑布流引发的思考)