深度优先遍历 递归_在houdini用OpenCL实现非递归的深度优先遍历法求解最大独立集问题...

6c1246ab3abf38b619243866ae5e12ee.png

这个算法“很简单”,用VEX可以“很快解决”但是用OCL做非常坑(尤其是debug)

我们不推荐大家使用OpenCL进行开发——SideFX

(本人只是个美术,术语不一定对)

其实你不懂深度优先搜索也没关系,其实我也不懂,就是自己想的,以前见过这个术语挺像的就套上去了(划掉)我尽量用自然语言讲解

偏炫技,实际解决大多数时候推荐用RBD,例如题图并不好看(划掉)

问题来源:(来自群)

我们用这个方法做出了一堆grid

67d708ef8140c0fd1665fcd3e610fb14.png

效果如下

87365423354845b993acf937ebf2dd76.png

其实就是vt中要用的decal

目标是

消除其中一定数量的grid

且尽可能少

做下面这个也是一样的道理

a35651e2f580afb0489bd4e3427badc5.png

输入如下:

92ed44f19069379eca318c450c7c3579.png

4e3008a9cfc472b4f1e6782aa46d0a66.png

d7a22ea1ae92541e5b97186590c9aa04.png

效果介绍完毕

制作思路:

这是一个处理各个grids(pieces)之间的关系问题

做多了数竞题(程序大佬应该也很快就想到啦)马上就能想到下面的,用图来解决

其实我只是想说,这个没啥难的,多做就很快就想到了,个人用时10分钟以内

我们将各个pieces抽象为一个点,有overlap关系的pieces连一条线,就构成了一个图

以这个为例

82588a618a77b0bb04f674a691c290f9.png

26d62863a1a33231378706a00e6d14f1.png

我们最后要的结果是,一个overlap的grid都没有,所以按道理这个这个图中应该一条边都没有,这个问题就是要求最大独立子集

换个说法,如果我们把 有overlap关系的pieces连一条线,如下

d0fb0f126188c90550fd0c54edc99147.png

我们就是要求这个图:点数最多的子图,且该图中各个点两两相连

也即是最大团问题

由于是一个NP问题,其实消耗挺大的,最好还是求较大团,下面开始炫技

这个的标准解法是Bron-Kerbosch算法

大概看了一下,我不会CPP,太难了,反正差不多就是递归一下,其实自己也能想到个大概,毕竟用vex习惯了写并行算法了

自己想的思路基本也八九不离十(

最基本的思路

用并行算法做这个,肯定首先是run over points,很自然问题会变成:

求解图里的最大团,且这个最大团包含@ptnum这个点

最后比较各个点的求解出来的“较大团”比较哪个团更大,选出来就好啦

再进一步,说白了对于run over的每个点这个问题其实是:

取出与该点相连的所有点组成的图G,求解G里的最大团

又是一个最大团问题,所以很自然想到递归

接下来是代码

其实用vex做这个是不太现实的,因为NP问题复杂度十分高,我们肯定要用OCL来做,但是OCL做这个发现带来前所未有的体验

我决定还是先用vex写一下大概的(虽然后面发现也基本没用)不过

vex正好可以做初步介绍

vex实在太简单了,我只用几十分钟就写好了,都不知道怎么讲解,一看就懂,自然语言

f014884306e8318c3f874eeaf6050cbf.png

当然,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也要被求出,大概就没问题了

输入:

fb7325c94f413ab84c83fbdb294c7287.png

用这个SOP处理

65990e80d3aa9ca0bb9f6a85e3b36c5b.png

输出(注意右上角的K4被group中了)

cb5141b20322e3010ec93a75261d86cf.png

节点图如下

4af2bc5703d62d00fc56c69e838fb5b9.png

进入OCL前,我们要先写入这些属性

88347c8f947d54968c6c9c0d2e99ea55.png

nebs是各个点的邻居,为了防止循环重复,我们要将ptnum小于自身的点都删掉

num max clique是搜索到的最大团的大小

sub max clique就是要存的包含当前点的最大团,我们要预先给入一个最大的可能长度,其实就是各个点的neighbourcount+1,注意这里要用push(-1)的方案,不能用resize(),因为这样默认值是0,而我们有0号点,所以不行

383a1ab8b02de7f01c0b4b9ac9c080aa.png

进入OCL

首先当然是来个史诗级的循环啦

千万不要用while(1),否则写错了,OCL根本停不下来,显示器都花瓶了,只能强制重启

e3243a1281c24780ef87de065390c20e.png

NumMaxLoop暴露为参数,默认1000

9f8e1d0e3d3a18bc293d4a8fc425903c.png

核心算法的文字讲解开始了

循环中,例如我们的起点是0号点,此时我们先搜索0号点的 第一个邻居 1号点

然后再搜索1号点的 第一个邻居 2号点,此时检查,2号点是否是0号点的邻居

如果是:搜索2号点的 第一个邻居 3号点,此时检查,3号点是否同时是0号点和1号点的邻居,只要是,0123号点就构成了一个完全图

以此类推

但是意外出现了:

3号点如果不是0号点的邻居,我们将 转而搜索 2号点的 第2个邻居

3a2547c0c16c7888ed162c99011eb2d9.png

但是呢,2号点如果没有第2个邻居,或者,2号点的所有邻居,都不能和0号点连接,我们将要回退到1号点,开始搜索 1号点 的 第2个邻居 4号点,可是我们怎么知道1号点此时该搜索第2个邻居呢?因此我们要用一个数组存下来(其实就是栈stack啦)

55f6c69a1cced99cc5152153d1dec0db.png

idx stack简称idxs,从前到后每一位数字代表了,一路搜索下来的点的,下一个将要搜索的点在邻居数组的序号。例如上图中,二号点对于一号点,序号为0,四号点对于一号点,序号为1。

如果搜索顺序的点顺序是014,那么idxs存的是01

因为:1号点对于0号点,在0号点的邻居数组中序号为0

因为:4号点对于1号点,在1号点的邻居数组中序号为1

如果搜索顺序的点顺序是0123,那么idxs存的是000

同时我们还要存一下,我们搜索到多深的位置,否则我们回退到上一个点,都不知道是数组中的哪一个位置诶,因此我们创建stacklen变量

1a250c4a67f96c9b42a07d84dc4f9208.png

其实也代表了搜索深度

3a2547c0c16c7888ed162c99011eb2d9.png

然后我们又发现,如果二号点走不通,转而走四号点的时候

四号点的定义是:一号点的第二个邻居

我们只存了idxs——邻居序号,也就是上面这句话的后半段

而前半段:一号点,一号点我们是怎么知道的呢

一号点的定义是:0号点的第1个邻居

禁止套娃!!!!

说白了就是,如果我们要知道各个点的序号,光有idxs确实够了,但是需要一个递归(套娃)的搜索,那可太累了,所以我们要多用一个数组point stack——简称pts[]来存放点的序号

也就是说

如果搜索顺序的点顺序是014,那么pts存的是014

如果搜索顺序的点顺序是0123,那么pts存的是0123

接下来开始写代码

首先是检测部分

38abce32577b747f72289b72e69a570b.png

注释写这么好,我一点也不北

上图中用到的find函数(不重要,和vex里的类似)

5c648fdd7760d1f6da098dd551411449.png

然后看后面,注意flag的作用

449c2e267f18ee00984c94f36a3a2993.png

当然现实不可能是总是能搜到

如果我们的脸足够黑,一直都搜索不到,一直不断idxs[stacklen] += 1

idxs[stacklen]都已经突破了邻居数组的长度,这时候我们要回退上一级了

这里涉及一个检测,邻居数组的长度,我们用一个同样用一个数组存好,也就是end stack——简称ends(上图的最后一行代码入栈,就是入的ends)

我们接下来看上面这些代码(这里就是循环全部的内容)

9b301b5d0e78d345249f04c42d6b9d03.png

kernel的循环前做的初始化

c2079f26f49a94053bee1c424281639b.png

三个栈的长度均为:各点的邻居数组的最大长度,因为最大搜索深度就是——有可能的最大完全子图的大小——也就是邻居数组长度的最大值

最后的优化,其实上面偷懒了,其实对于run over的每个点,所需要的栈的长度不同,其实只需要:自己这个点的邻居数组的长度,即可。之所以上面那样写是因为,为了大家好理解

e35207766cdc379147d55b5c00f3a121.png

b4fcb6bc76a0a21dcbb43c0fc8b92cd8.png
别抄了,抄不来(划掉)

此时最大团算法的OCL部分结束

VEX善后,比较各个点的最大团,哪个更大

8ab4df9365e89611cda727b464076c02.png

此时我们就做完了下面的HDA

559b03c6d412daef6095adc2e6db6570.png

当然我们要做题图的效果还没完,我们是要做这个HDA

FeE Deoverlap

c363e16ec2c9af17a1ad7e38fbad4d2c.png

也就是把题图的输入,抽象为一个图

这个实在是太简单了,我只用了十多分钟就做出来了,都能看懂那个OCL了,这个真的是弱智问题

FeE Deoverlap内部:

16e59fc5492e6da351a5f031c42b5e2d.png

注:boolean SOP选detect参数可以检测上游的布尔关系

2a871ac8834a78d3451d440a2339ada7.png

tips就是最后用point generate SOP来抽象(毕竟图的底层只是一个连接关系的矩阵数据而已,@P没用),而不是extract centroid SOP(只是做可视化方便)

经常做并行算法肯定对这个操作了如指掌啦,也不必多说


欢迎大佬来加QQ houdini程序化设计群642672039(QQ最温柔的程序化讨论群)

加群仅限houdini程序化建模设计/流程向大佬,需要审核,严禁内鬼

7db3180e29e66d0b57f98b4cac75a616.png

群规: 原则上禁水,且禁新手问题,但是讨论有趣的事情也可以;

希望大家多分享的群,除非认识,否则一个月不说话就不能继续待着啦,只是为了防内鬼,不用那么紧张啦,可以再加回来的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值