java实现梁友栋裁剪算法_裁剪算法 - Jing_Rui - 博客园

Cohen-Sutherland算法(编码裁剪算法)

一、基本思想

采用编码的方式对直线段分三种情况处理

dd9e961928f8ffa83265fd06aa42a2fb.png

3457ea66122ab98d4af343d055c7456a.png

(3)重点在于2种情况都不满足的时候,需要按交点来进行分段,然后再判定。

二、编码规则

(1)以上三种情况都采用编码的方式来快速解决。

每条线段的端点偶读赋以四位二进制码D3D2D1D0,编码规则如下:

d3dd5d170f2f122f8305e5c9f56bac97.png

左右下上,即边界之外为1,否则为0。

(2)

5326b4a444081dbe96819565f89de27a.png

三、具体实现

裁剪出一条线段,先求出端点编码code1和code2,然后进行“或” 和“与” 运算。

d5c9325de33a69201f3fb76da629ad3e.png

或0取,与非弃。

(3)若以上2种情况都不满足,例如:

dfe1e93f49e8aa539c6bbd8c92826247.png

此时将p1和p2的编码进行与或操作发现,或不为0,与为0。则采用分段的方法。按左右下上的顺序求出交点P3,P1P3在窗外则舍弃。

ab9b507db8df1903c2241762f21e74de.png

得到p3p2时,再对p3p2进行编码测试,发现也属于第三种情况。则再求出交点p4。经测试,p3p4可取,p4p2舍弃。

四、额外说明

(1)虚交点:对于舍弃的情况还有一种特例,如下图中的蓝线。

3654bb053c1eb878de3f3fdab3158d24.png

此时AB点进行编码测试也属于第三种情况,但是却需要舍弃。这就需要计算出虚交点,然后分段舍弃了AC和CB。

(2)交点的计算方法

因为通常都是已经边界坐标,所以都是利用斜率来按照左下右上的顺序计算交点。

28118050ac1f38b4139a0a60f0d0a6ec.png

中点分割算法

一、基本思想

中点分割用到的测试方法仍然是编码测试,但是用二分逼近来确定直线段与窗口的交点。而具体的实现,也是根据中点的位置情况来讨论的。

二、具体实现

fee55c85106345b5ac1ffdb157b053ec.png

d5a520cfb2f84e1b8cde7d035fbc500c.png

何为误差范围?

比如在1024的分辨率下,最多也就进行10次二分,不可能永远分下去。所以需要提前设定误差范围。

中点分割算法相比于Cohen-Sutherland算法,避免了求交,只需计算中点坐标即可完成,宜于硬件实现 。

Liang-Barsky算法

一、基本思想

把被裁剪直线看成一条有向线段并用参数方程表示,进而确定出要获取的部分。

1.参数表示

7d449905d0d46dbc5fc6713bacf79422.png

d2409975ec685fccfafbaedd3b4d222e.png

2.方向设定确认点

将四条边分为入边和出边

724a37bf127f3261a5b5c0461a8d3f17.png

u1u2则为裁剪区域内的线段

3.不等式变换

81ed59a4b13489f756ccfff8e0eae81e.png

裁剪区域内的点可由以上不等式得到,移项变换得

648d4c3af455b3e1d0321b1606191e78.png

我们可以

f26c0def99c410a95c1464a7ba147267.png

4.分类讨论

(1)pk=0

a. p1&p2=0,此时直线垂直,若q1<0或q2<0则直线在裁剪区域外,直接舍弃。

0fe86251dca5fab23b86899d949d86d2.png

b.  p3&p4=0,此时直线平行,若q3<0或q4<0则直线在裁剪区域外,直接舍弃。

ca69a418460ac3bf1cadb60fb5b6b3b3.png

c.  p1&p2=0且q1≥0或q2≥0,p3&p4=0且q3≥0或q4≥0(即上图B和F的情况)。则进一步根据交点判断。

(2)pk≠0

f6daf1f2dfe09f9bda3e2894bdca32e1.png

此处根据具体的ΔxΔy数据来得到pk的正负

dae9bc267cfb1dfd5e74cd1fa6feab55.png

3b276a48a14083cbe328a60c12180919.png

e7d72fbed0a90be0405dda1bbee55011.png

如图,此种情况为舍弃。u1>u2

二、具体实现

a52dc0f8afc7842bb9911bab4fc7f40b.png

ad5886a142eb04176dcfe3c671c7d082.png

60e9649cdece06d2d8eadd60f51d1115.png

581a6fec2edcdef150f836e0df621f35.png

397ad84fb6fa9c0f01ad88c927ef9a94.png

6e40b0ac0320059f4e651f68ef9f989c.png

LFM算法是一种基于矩阵分解的推荐算法,下面给出Java代码实现。 首先,我们需要定义一个User类和Item类来表示用户和物品: ```java public class User { private int id; // 用户ID private List<Item> items; // 用户评价的物品列表 // 省略构造函数和getter/setter方法 } public class Item { private int id; // 物品ID private double rating; // 用户对物品的评分 // 省略构造函数和getter/setter方法 } ``` 然后,我们定义LFM算法类: ```java public class LFM { // 根据用户评价的物品列表计算用户的隐向量 public double[] getUserVector(List<Item> items) { // 省略实现 } // 根据物品被哪些用户评价过的列表计算物品的隐向量 public double[] getItemVector(List<User> users) { // 省略实现 } // 计算两个向量的点积 public double dotProduct(double[] v1, double[] v2) { // 省略实现 } // 计算两个向量的余弦相似度 public double cosineSimilarity(double[] v1, double[] v2) { // 省略实现 } // 预测用户对物品的评分 public double predictRating(User user, Item item) { // 省略实现 } } ``` 在实现LFM算法的过程中,需要使用矩阵分解技术,这里使用SVD算法来进行矩阵分解。由于Java本身不提供SVD算法实现,我们可以使用第三方库来进行SVD分解,比如Jama库。具体实现可以参考以下代码: ```java import Jama.*; public class LFM { // LFM算法参数 private int factorNum; // 隐向量的维度 private double alpha; // 学习率 private double lambda; // 正则化参数 // 保存用户和物品的隐向量 private Map<Integer, double[]> userVectors; private Map<Integer, double[]> itemVectors; public LFM(int factorNum, double alpha, double lambda) { this.factorNum = factorNum; this.alpha = alpha; this.lambda = lambda; userVectors = new HashMap<Integer, double[]>(); itemVectors = new HashMap<Integer, double[]>(); } // 根据用户评价的物品列表计算用户的隐向量 public double[] getUserVector(List<Item> items) { Matrix matrix = new Matrix(items.size(), factorNum); for (int i = 0; i < items.size(); i++) { double[] itemVector = itemVectors.get(items.get(i).getId()); for (int j = 0; j < factorNum; j++) { matrix.set(i, j, itemVector[j]); } } SingularValueDecomposition svd = matrix.svd(); double[] userVector = new double[factorNum]; for (int i = 0; i < factorNum; i++) { userVector[i] = svd.getV().get(i, factorNum - 1); } return userVector; } // 根据物品被哪些用户评价过的列表计算物品的隐向量 public double[] getItemVector(List<User> users) { Matrix matrix = new Matrix(users.size(), factorNum); for (int i = 0; i < users.size(); i++) { double[] userVector = userVectors.get(users.get(i).getId()); for (int j = 0; j < factorNum; j++) { matrix.set(i, j, userVector[j]); } } SingularValueDecomposition svd = matrix.svd(); double[] itemVector = new double[factorNum]; for (int i = 0; i < factorNum; i++) { itemVector[i] = svd.getV().get(i, factorNum - 1); } return itemVector; } // 计算两个向量的点积 public double dotProduct(double[] v1, double[] v2) { double result = 0.0; for (int i = 0; i < v1.length; i++) { result += v1[i] * v2[i]; } return result; } // 计算两个向量的余弦相似度 public double cosineSimilarity(double[] v1, double[] v2) { double dotProduct = dotProduct(v1, v2); double normV1 = Math.sqrt(dotProduct(v1, v1)); double normV2 = Math.sqrt(dotProduct(v2, v2)); return dotProduct / (normV1 * normV2); } // 预测用户对物品的评分 public double predictRating(User user, Item item) { double[] userVector = userVectors.get(user.getId()); double[] itemVector = itemVectors.get(item.getId()); return dotProduct(userVector, itemVector); } // 训练LFM算法 public void train(List<User> users, List<Item> items) { // 初始化用户和物品的隐向量 for (User user : users) { double[] userVector = new double[factorNum]; for (int i = 0; i < factorNum; i++) { userVector[i] = Math.random() / Math.sqrt(factorNum); } userVectors.put(user.getId(), userVector); } for (Item item : items) { double[] itemVector = new double[factorNum]; for (int i = 0; i < factorNum; i++) { itemVector[i] = Math.random() / Math.sqrt(factorNum); } itemVectors.put(item.getId(), itemVector); } // 迭代优化隐向量 for (int step = 0; step < 100; step++) { for (User user : users) { double[] userVector = getUserVector(user.getItems()); userVectors.put(user.getId(), userVector); } for (Item item : items) { double[] itemVector = getItemVector(item.getUsers()); itemVectors.put(item.getId(), itemVector); } for (User user : users) { for (Item item : items) { if (user.getItems().contains(item)) { double rui = item.getRating(); double[] userVector = userVectors.get(user.getId()); double[] itemVector = itemVectors.get(item.getId()); double pui = dotProduct(userVector, itemVector); double eui = rui - pui; for (int i = 0; i < factorNum; i++) { userVector[i] += alpha * (eui * itemVector[i] - lambda * userVector[i]); itemVector[i] += alpha * (eui * userVector[i] - lambda * itemVector[i]); } userVectors.put(user.getId(), userVector); itemVectors.put(item.getId(), itemVector); } } } } } } ``` 以上就是LFM算法Java代码实现。在实际使用中,我们可以根据数据量的大小选择不同的SVD算法实现,比如对于小规模数据集可以直接使用Jama库,对于大规模数据集可以使用Spark中的MLlib库的SVD算法实现。此外,我们也可以根据数据特点进行优化,比如引入建模技巧和特征选择等,以提高算法的推荐效果和运算速度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值