如何在功能性分而治之 Java 算法中实现“分”功能?
Posted
技术标签:
【中文标题】如何在功能性分而治之 Java 算法中实现“分”功能?【英文标题】:How can I implement the function "divide" in a functional Divide-And-Conquer Java algorithm? 【发布时间】:2018-06-23 11:11:20 【问题描述】:我正在尝试实现快速排序算法的功能版本。 我的教授让我留下这个作为签名:
public static <T, R> List<T> myQuickSort(Function<List<T>, Boolean> trivial,
Function<List<T>, T> solve, Function<List<T>, R> divide,
Function<T, List<R>> combine, List<T> input)
我创建了一个名为 Pair 的辅助类,如下所示:
public class Pair
List<Integer> first;
List<Integer> second;
Pair(List<Integer> f, List<Integer> s)
first = f;
second = s;
public static Pair div(List<Integer> input)
int pivot = (int) input.get(0);
List<Integer> a = new ArrayList<Integer>();
List<Integer> b = new ArrayList<Integer>();
for(int i=1; i<input.size(); i++)
if(input.get(i) < pivot)
a.add(input.get(i));
else
b.add(input.get(i));
return new Pair(a, b);
我快完成了,但我无法弄清楚如何在输入数组的单个分区上递归工作。我试着这样做:
if(trivial.apply(input))
solve.apply(input);
else
output = myQuickSort(trivial, solve, divide, combine,
(List<T>) divide.apply(input).first);
output.add(input.get(0));
output.addAll(myQuickSort(trivial, solve, divide, combine,
(List<T>) divide.apply(input).second));
return output;
return output;
但我现在卡住了。 你们中的任何人都可以告诉我哪里错了和/或我怎样才能更好地实施我的解决方案? 这也是主要的,如果它可以帮助:
Function<List<Integer>, Boolean> trivial = (a) -> a.size()==1;
Function<List<Integer>, Pair> divide = (a) -> Pair.div(input);
Function<Pair, List<Integer>> combine =
(a) -> Stream.concat(a.first.stream(), a.second.stream()).
collect(Collectors.toList());
Function<List<Integer>, Integer> solve = (a) -> a.get(0);
ArrayList<Integer> output = myQuickSort(trivial, solve, divide, combine, input);
【问题讨论】:
签名不是有bug吗?如果combine
的类型是Function<R, List<T>>
,对我来说会更有意义。
我做了一些修改,现在这是签名:“public static div
返回三个列表而不是一对列表会更简单、更有效:class SplitByPivot List<T> equal, less, greater;
(不要忘记枢轴可以在输入中多次出现。 ) 我很确定这个错误是在你隐式处理枢轴作为Pair
中的一个棘手的特殊情况的方式中。 equal
字段将让您无需技巧地表达算法。
等等——你说你的教授要求你保留那个签名,现在你改了?甚至您的更改签名也存在根本问题,即 调用者 决定 R
是什么,而不是方法。因此,您不能假设它始终是Pair
。甚至不清楚你是如何在隐含假设 R
是 Pair
的情况下编译代码的。你应该先和你的教授讨论那个奇怪的签名。
【参考方案1】:
提出了很多问题。所以我能做的就是解决问题。
我假设在您的 myQuickSort 签名中,combine 的类型已交换了 R 和 T。如果我是对的,我会发现这个签名很奇怪,如下所述。
代码中的“solve.apply()”行对返回没有任何作用。由于您不使用对该函数的调用返回,因此我无法确定“解决”的目的是什么。或许你可以澄清一下。结果,您的最后一个“返回输出”不会返回任何内容。在下面的讨论中,我忽略了“解决”。
您在 main 中实现的“组合”不必要地使用了流。 List.addAll() 可以解决问题。
您没有在实现中使用 combine 函数。
我的问题是关于你的教授给你的签名。在我看来,R 是一个代表一分为二的列表的对象。 R 将包括第一个和第二个列表,以及您所说的“枢轴”。然后,Combine 接受一个 R 并将这三个部分组合成一个列表。由于 R、divide 和 combine 的实现是由 myQuickSort 的调用者完成的,它们将处理正在排序的对象的比较(您的实现中的整数),因此 myQuickSort 不必知道正在排序的对象的类型排序。
如果我是对的,那么在我看来,您的 R 实现(称为 Pair)应该存储枢轴。然后,您的 combine 实现将结合 first、pivot 和 second。除或组合都会调用 myQuickSort: 无所谓。你有分工,这很好。
myQuickSort 然后将是:
if (trivial(input))
return input; // Remember I am ignoring solve()
else
R r = divide.apply(input);
return combine.apply(r);
因此,除或组合的实现(在您的情况下是除法)通过为已划分列表的两半中的每一半调用 myQuickSort 来提供递归。
如果我在上面是对的,这是一个奇怪的签名。提供的函数必须了解 QuickSort 实现。如果 R 被定义为具有 myQuickSort 可以操作分割数组的方法,则情况并非如此。这意味着,与其简单地在静态方法签名中使用 R,不如使用“R extends DivObject”,其中 DivObject 是定义 myQuickSort 可以调用的方法的接口(或类)。我会让 DivObject 有一个 getFirst() 方法和一个 getSecond() 方法,它们返回相应的列表,以便 myQuickSort 可以在这两个列表上调用自己。它还需要像 setFirst() 和 setSecond() 这样的方法,将排序后的列表放回 R 中,以便 combine 可以组合它们(因为 combine 仅将 R 作为参数)。
希望这会有所帮助。
【讨论】:
solve() 应该尝试替换“return”语句,但我现在注意到它没用。关于签名,是的,这很奇怪,这就是我遇到这个困难的原因 我尝试了你的方法,但是看起来“Pair”的第二部分没有被触及。你知道为什么吗? 我真的无法调试你的代码。我的建议并不是唯一需要的改变。正如我所说,divide 或 combine 函数都需要在 first 和 second 上调用 myQuickSort 并适当地使用结果。以上是关于如何在功能性分而治之 Java 算法中实现“分”功能?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 iOS Pin It SDK 在 Pinterest 中实现 Followboard 按钮功能?