这个算法“很简单”,用VEX可以“很快解决”但是用OCL做非常坑(尤其是debug)
我们不推荐大家使用OpenCL进行开发——SideFX
(本人只是个美术,术语不一定对)
其实你不懂深度优先搜索也没关系,其实我也不懂,就是自己想的,以前见过这个术语挺像的就套上去了(划掉)我尽量用自然语言讲解
偏炫技,实际解决大多数时候推荐用RBD,例如题图并不好看(划掉)
问题来源:(来自群)
我们用这个方法做出了一堆grid
效果如下
其实就是vt中要用的decal
目标是
消除其中一定数量的grid
且尽可能少
做下面这个也是一样的道理
输入如下:
效果介绍完毕
制作思路:
这是一个处理各个grids(pieces)之间的关系问题
做多了数竞题(程序大佬应该也很快就想到啦)马上就能想到下面的,用图来解决
其实我只是想说,这个没啥难的,多做就很快就想到了,个人用时10分钟以内
我们将各个pieces抽象为一个点,有overlap关系的pieces连一条线,就构成了一个图
以这个为例
我们最后要的结果是,一个overlap的grid都没有,所以按道理这个这个图中应该一条边都没有,这个问题就是要求最大独立子集
换个说法,如果我们把 没 有overlap关系的pieces连一条线,如下
我们就是要求这个图:点数最多的子图,且该图中各个点两两相连
也即是最大团问题
由于是一个NP问题,其实消耗挺大的,最好还是求较大团,下面开始炫技
这个的标准解法是Bron-Kerbosch算法
大概看了一下,我不会CPP,太难了,反正差不多就是递归一下,其实自己也能想到个大概,毕竟用vex习惯了写并行算法了
自己想的思路基本也八九不离十(
最基本的思路
用并行算法做这个,肯定首先是run over points,很自然问题会变成:
求解图里的最大团,且这个最大团包含@ptnum这个点
最后比较各个点的求解出来的“较大团”比较哪个团更大,选出来就好啦
再进一步,说白了对于run over的每个点这个问题其实是:
取出与该点相连的所有点组成的图G,求解G里的最大团
又是一个最大团问题,所以很自然想到递归
接下来是代码
其实用vex做这个是不太现实的,因为NP问题复杂度十分高,我们肯定要用OCL来做,但是OCL做这个发现带来前所未有的体验
我决定还是先用vex写一下大概的(虽然后面发现也基本没用)不过
vex正好可以做初步介绍
vex实在太简单了,我只用几十分钟就写好了,都不知道怎么讲解,一看就懂,自然语言
当然,vex是不支持递归的,写了只是给自己心理安慰
我还写了个python生成VEX的,没写完(因为深度如果打到几十层,那几十个for的代码量会突破天际)
写给houdini的OpenCL新手
本篇不是OCL新手教程
学houdini的OCL,途径就是B站捷佳大佬的免费教程(强烈推荐),以及vimeo的hou官方的 *唯一* 一个视频教程,以及一个vimeo的俄语教程(开放HIP)
剩下的就是到C:Program FilesSide Effects SoftwareHoudini 18.0.309houdiniocl
找规律编程
OCL实现,十分丧病,用时4天,极其极其难
最后我没用递归,因为OCL递归太难写了,写了一天决定放弃,头发都要掉光了,我们将会用非递归式的深度优先搜索来做这个
以这个为测试,最后目标是右上角的K4,同时左下的两个K3也要被求出,大概就没问题了
输入:
用这个SOP处理
输出(注意右上角的K4被group中了)
节点图如下
进入OCL前,我们要先写入这些属性
nebs是各个点的邻居,为了防止循环重复,我们要将ptnum小于自身的点都删掉
num max clique是搜索到的最大团的大小
sub max clique就是要存的包含当前点的最大团,我们要预先给入一个最大的可能长度,其实就是各个点的neighbourcount+1,注意这里要用push(-1)的方案,不能用resize(),因为这样默认值是0,而我们有0号点,所以不行
进入OCL
首先当然是来个史诗级的循环啦
千万不要用while(1),否则写错了,OCL根本停不下来,显示器都花瓶了,只能强制重启
NumMaxLoop暴露为参数,默认1000
核心算法的文字讲解开始了
循环中,例如我们的起点是0号点,此时我们先搜索0号点的 第一个邻居 1号点
然后再搜索1号点的 第一个邻居 2号点,此时检查,2号点是否是0号点的邻居
如果是:搜索2号点的 第一个邻居 3号点,此时检查,3号点是否同时是0号点和1号点的邻居,只要是,0123号点就构成了一个完全图
以此类推
但是意外出现了:
3号点如果不是0号点的邻居,我们将 转而搜索 2号点的 第2个邻居
但是呢,2号点如果没有第2个邻居,或者,2号点的所有邻居,都不能和0号点连接,我们将要回退到1号点,开始搜索 1号点 的 第2个邻居 4号点,可是我们怎么知道1号点此时该搜索第2个邻居呢?因此我们要用一个数组存下来(其实就是栈stack啦)
idx stack简称idxs,从前到后每一位数字代表了,一路搜索下来的点的,下一个将要搜索的点在邻居数组的序号。例如上图中,二号点对于一号点,序号为0,四号点对于一号点,序号为1。
如果搜索顺序的点顺序是014,那么idxs存的是01
因为:1号点对于0号点,在0号点的邻居数组中序号为0
因为:4号点对于1号点,在1号点的邻居数组中序号为1
如果搜索顺序的点顺序是0123,那么idxs存的是000
同时我们还要存一下,我们搜索到多深的位置,否则我们回退到上一个点,都不知道是数组中的哪一个位置诶,因此我们创建stacklen变量
其实也代表了搜索深度
然后我们又发现,如果二号点走不通,转而走四号点的时候
四号点的定义是:一号点的第二个邻居
我们只存了idxs——邻居序号,也就是上面这句话的后半段
而前半段:一号点,一号点我们是怎么知道的呢
一号点的定义是:0号点的第1个邻居
禁止套娃!!!!
说白了就是,如果我们要知道各个点的序号,光有idxs确实够了,但是需要一个递归(套娃)的搜索,那可太累了,所以我们要多用一个数组point stack——简称pts[]来存放点的序号
也就是说
如果搜索顺序的点顺序是014,那么pts存的是014
如果搜索顺序的点顺序是0123,那么pts存的是0123
接下来开始写代码
首先是检测部分
注释写这么好,我一点也不北
上图中用到的find函数(不重要,和vex里的类似)
然后看后面,注意flag的作用
当然现实不可能是总是能搜到
如果我们的脸足够黑,一直都搜索不到,一直不断idxs[stacklen] += 1
idxs[stacklen]都已经突破了邻居数组的长度,这时候我们要回退上一级了
这里涉及一个检测,邻居数组的长度,我们用一个同样用一个数组存好,也就是end stack——简称ends(上图的最后一行代码入栈,就是入的ends)
我们接下来看上面这些代码(这里就是循环全部的内容)
kernel的循环前做的初始化
三个栈的长度均为:各点的邻居数组的最大长度,因为最大搜索深度就是——有可能的最大完全子图的大小——也就是邻居数组长度的最大值
最后的优化,其实上面偷懒了,其实对于run over的每个点,所需要的栈的长度不同,其实只需要:自己这个点的邻居数组的长度,即可。之所以上面那样写是因为,为了大家好理解
此时最大团算法的OCL部分结束
VEX善后,比较各个点的最大团,哪个更大
此时我们就做完了下面的HDA
当然我们要做题图的效果还没完,我们是要做这个HDA
FeE Deoverlap
也就是把题图的输入,抽象为一个图
这个实在是太简单了,我只用了十多分钟就做出来了,都能看懂那个OCL了,这个真的是弱智问题
FeE Deoverlap内部:
注:boolean SOP选detect参数可以检测上游的布尔关系
tips就是最后用point generate SOP来抽象(毕竟图的底层只是一个连接关系的矩阵数据而已,@P没用),而不是extract centroid SOP(只是做可视化方便)
经常做并行算法肯定对这个操作了如指掌啦,也不必多说
欢迎大佬来加QQ houdini程序化设计群642672039(QQ最温柔的程序化讨论群)
加群仅限houdini程序化建模设计/流程向大佬,需要审核,严禁内鬼
群规: 原则上禁水,且禁新手问题,但是讨论有趣的事情也可以;
希望大家多分享的群,除非认识,否则一个月不说话就不能继续待着啦,只是为了防内鬼,不用那么紧张啦,可以再加回来的