假设您有 25个对象,并且机器可以按照您甚至不知道的某些标准对其中的5个进行排序。使用这台机器的成本非常昂贵(一次发射需要1美元),那么分类所有物品的最低成本是多少?
我目前的解决方案非常简单(与merge sort类似的想法):
因此,一般来说,我们必须支付 26 $ (26次发布)。
问题:有没有办法让它更便宜(以最少的发布次数排序)?
答案 0 :(得分:8)
这是一个贪心算法,用于选择在每次迭代时对哪些对象进行排序:
对25个对象进行排序 i 与完全填充表M 25x25 相同,其中M i,j = 1如果< sub> i &gt; a j ,否则为-1。在使用机器执行单次迭代排序后,您可以获得刚刚排序的元素之间的直接关系(最多可立即填充5个单元格),但之后您可以使用可交换性填充更多单元格(即,如果a> b,然后你知道b&lt; a)和传递性(即,如果a> b和b&gt; c,那么你知道a&gt; c)。
要为下一个排序选择5个元素,请选择元素,对应于这些元素的行和列之间的交叉点中有大多数空单元格。为此,您可以比较所有可能的组合。有25个选择5 = 53130个可能的变体,复杂性实际上是指数级的,但这不会花费任何“金钱”这个问题。
当表格完全填满时,您可以使用Topological sort构建排序序列,或者简单地通过相应表格行中值的总和对元素进行排序:总和越高,元素越大。
这不是理想的,但非常有效。我已经在随机排列上测试了这种方法,结果平均约为16.8美元。这是Python中的代码示例:
import random
import itertools
class SortingMachine:
def __init__(self):
self.coins = 0
def sort(self, elements):
assert(len(elements) == 5)
self.coins += 1
return list(sorted(elements))
def test_sorting(seq):
N = len(seq)
machine = SortingMachine()
table = [[0 if i == j else None for j in range(N)] for i in range(N)]
# Fill empty table cells using transitivity with Floyd-Warshall algorithm
def fill_transitive():
for k in range(N):
for i in range(N):
for j in range(N):
if table[i][j] is None and table[i][k] == table[k][j]:
table[i][j] = table[i][k]
# Register in the table the information that seq[i] > seq[j]
def set_greater(i, j):
table[i][j] = 1
table[j][i] = -1
# Register in the table information from 5 sorted elements
def register_sorted(sub):
for (el1, i1), (el2, i2) in zip(sub, sub[1:]):
set_greater(i2, i1)
# Select 5 elements to send to the machine
def choose_elements():
# Count empty cells in the cells corresponding to 5 comb elements
def empty_cells(comb):
return sum(table[i][j] is None
for i, el1 in comb for j, el2 in comb)
comb = max((empty_cells(comb), comb)
for comb in itertools.combinations(enumerate(seq), 5))[1]
return [(el, ind) for ind, el in comb]
# Return True if the table is completely filled
def is_complete():
return all(all(el is not None for el in row) for row in table)
while not is_complete():
chosen = choose_elements()
sorted_chosen = machine.sort(chosen)
register_sorted(sorted_chosen)
fill_transitive()
# Checking that the sorting is correct
sorted_seq = list(sorted(seq))
assert(all(sorted_seq.index(seq[ind]) == (sum(row) + N - 1) // 2
for ind, row in enumerate(table)))
return machine.coins
def random_sequence():
l = list(range(25))
random.shuffle(l)
return l
这种方法中的贪心启发式只能最大化从排序中获得的即时信息,而不考虑传递性。现在,理论上一个更好的启发式方法是最大化预期信息,对所选择的5个元素进行排序,包括传递性获得的所有信息。也就是说,选择5个元素,在考虑传递性之后,使用最大平均值(在它们的所有可能的排序结果上)填充单元格的数量。但是实现这种算法的天真算法需要更长的时间来计算。
答案 1 :(得分:4)
您显然没有很好地使用这些信息。让我们说A1,B1,C1,D1,E1是他们小组中最小的,你刚刚发现D1是最小的整体。然后排序A1,B1,C1,D2和E1。这显然是低效的,因为你知道其中四个的顺序。
让我们说他们是按顺序D1,A1,C1,E1,B1出来的。你删除了D1。哪些项目可以是最小的?只有A1和D2。哪些项目可以是第二小的?只有A1,C1,D2,D3和A2。所以你对这五个进行排序,两个最小的整体是最小的。
在那之后,情况有点复杂,但我们肯定能找到最大的一个,然后是下一个最大的那个,然后又是下一个最大的等等,所以我们有五个初始排序,另外14个排序找到21个最小,最后一个=总共20个。我们可能做得更好,但后来变得复杂了。
答案 2 :(得分:2)
我已经尝试过这个问题的另一个想法:像Quicksort这样的东西。给定一组以这种方式排序的项目,可能有一些关于元素之间的顺序的部分信息,我们尝试在集合中间附近找到一个好的“枢轴”,然后执行排序操作,将4个其他项目与枢轴进行比较。为了将集合分成小于枢轴的子集和大于枢轴的子集。然后我们以类似的方式对每个子集进行排序。
最初我们没有关于订单的任何信息,所以我们随机挑选五个元素并对它们进行排序。然后这五个中间项目是一个枢轴的好选择。然后,随着更多信息的出现,我们可以继续使用该支点或修改我们的选择。
下面我已经包含了Java类来帮助可视化过程并收集有关选择数据透视的各种方法的统计信息。有关课程的一般概述,请参阅我的其他答案。 (从那以后我对它们进行了一些改进。)如果你想切换排序方法,你必须编辑FiveSortPanel类。
使用各种不同的枢轴方法平均需要16.85到16.95步,并且从不超过20步(在我随机抽取的样本中)。此外,该方法非常快,仅使用线性时间启发式查找枢轴,其他四个项目在每个步骤中进行排序。
以下是修订后的GUI框架:
package org.jgrapht.demo;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class FiveSort extends JFrame {
private static final long serialVersionUID = 1L;
private Font smallFont = new Font(Font.DIALOG, Font.PLAIN, 12);
private Font largeFont = new Font(Font.DIALOG, Font.PLAIN, 36);
private JLabel stepsLabel = new JLabel("0");
private JLabel maxLabel = new JLabel("0");
private JLabel averageLabel = new JLabel("");
private int rounds = 0;
private int totalSteps = 0;
private double averageSteps = 0;
private int maxSteps = 0;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new FiveSort();
}
});
}
public FiveSort() {
initGUI();
setLocationRelativeTo(null);
setTitle("Five Sort");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public void initGUI() {
try {
// UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName() );
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
} catch (Exception e) {
e.printStackTrace();
}
// title label
JLabel titleLabel = new JLabel("Five Sort");
titleLabel.setFont(largeFont);
titleLabel.setBackground(Color.BLACK);
titleLabel.setForeground(Color.WHITE);
titleLabel.setOpaque(true);
titleLabel.setHorizontalAlignment(JLabel.CENTER);
add(titleLabel,BorderLayout.PAGE_START);
// main panel
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel,BoxLayout.Y_AXIS));
add(mainPanel);
// graph panel
FiveSortPanel graphPanel = new FiveSortPanel();
mainPanel.add(graphPanel,BorderLayout.CENTER);
// stats panel
JPanel statsPanel = new JPanel();
statsPanel.setBackground(Color.BLACK);
mainPanel.add(statsPanel);
JLabel stepsTitleLabel = new JLabel("Current Steps: ");
stepsTitleLabel.setFont(smallFont);
stepsTitleLabel.setForeground(Color.WHITE);
statsPanel.add(stepsTitleLabel);
stepsLabel.setFont(largeFont);
stepsLabel.setForeground(Color.WHITE);
stepsLabel.setText("" + graphPanel.getSteps());
statsPanel.add(stepsLabel);
JLabel maxTitleLabel = new JLabel("Max Steps: ");
maxTitleLabel.setFont(smallFont);
maxTitleLabel.setForeground(Color.WHITE);
statsPanel.add(maxTitleLabel);
maxLabel.setFont(largeFont);
maxLabel.setForeground(Color.WHITE);
maxLabel.setText("" + maxSteps);
statsPanel.add(maxLabel);
JLabel averageTitleLabel = new JLabel("Avg Steps: ");
averageTitleLabel.setFont(smallFont);
averageTitleLabel.setForeground(Color.WHITE);
statsPanel.add(averageTitleLabel);
averageLabel.setFont(largeFont);
averageLabel.setForeground(Color.WHITE);
averageLabel.setText("");
statsPanel.add(averageLabel);
// button panel
JPanel buttonPanel = new JPanel();
buttonPanel.setBackground(Color.BLACK);
add(buttonPanel,BorderLayout.PAGE_END);
Button newButton = new Button("Step");
newButton.setFocusable(false);
newButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!graphPanel.isComplete()) {
graphPanel.step();
stepsLabel.setText("" + graphPanel.getSteps());
}
}
});
buttonPanel.add(newButton);
Button restartButton = new Button("Restart");
restartButton.setFocusable(false);
restartButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (graphPanel.isComplete()) {
++rounds;
totalSteps += graphPanel.getSteps();
averageSteps = ((int)(totalSteps / (double)rounds * 10))/10.0;
maxSteps = Math.max(maxSteps, graphPanel.getSteps());
}
graphPanel.restart();
stepsLabel.setText("" + graphPanel.getSteps());
maxLabel.setText("" + maxSteps);
averageLabel.setText("" + averageSteps);
}
});
buttonPanel.add(restartButton);
Button run50Button = new Button("Run 50");
run50Button.setFocusable(false);
run50Button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int currentRounds = 0;
while (currentRounds < 50) {
if (!graphPanel.isComplete()) {
graphPanel.step();
stepsLabel.setText("" + graphPanel.getSteps());
} else {
++rounds;
++currentRounds;
totalSteps += graphPanel.getSteps();
averageSteps = ((int)(totalSteps / (double)rounds * 100))/100.0;
maxSteps = Math.max(maxSteps, graphPanel.getSteps());
graphPanel.restart();
stepsLabel.setText("" + graphPanel.getSteps());
maxLabel.setText("" + maxSteps);
averageLabel.setText("" + averageSteps);
}
}
}
});
buttonPanel.add(run50Button);
}
}
这是经过修改的图形操作例程(需要jgrapht-ext-0.9.1-uber.jar,可从JGraphT网站免费获得):
package org.jgrapht.demo;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.JPanel;
import org.jgrapht.DirectedGraph;
import org.jgrapht.Graphs;
import org.jgrapht.ListenableGraph;
import org.jgrapht.alg.StrongConnectivityInspector;
import org.jgrapht.alg.TransitiveClosure;
import org.jgrapht.ext.JGraphXAdapter;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.ListenableDirectedGraph;
import org.jgrapht.graph.SimpleDirectedGraph;
import com.mxgraph.layout.mxCircleLayout;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
public class FiveSortPanel extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ARRAY_SIZE = 25;
private static final int SORT_SIZE = 5;
private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String STROKE_YELLOW = "strokeColor=#CCCC00";
private Integer[][] BIBD = {
{1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15}, {16,17,18,19,20}, {21,22,23,24,0},
{1,6,11,16,21}, {2,7,12,17,21}, {3,8,13,18,21}, {4,9,14,19,21}, {5,10,15,20,21},
{2,8,14,20,22}, {3,10,11,19,22}, {5,9,12,16,22}, {1,7,15,18,22}, {4,6,13,17,22},
{3,9,15,17,23}, {5,6,14,18,23}, {4,7,11,20,23}, {2,10,13,16,23}, {1,8,12,19,23},
{4,10,12,18,24}, {1,9,13,20,24}, {2,6,15,19,24}, {5,8,11,17,24}, {3,7,14,16,24},
{5,7,13,19,0}, {4,8,15,16,0}, {1,10,14,17,0}, {3,6,12,20,0}, {2,9,11,18,0}
};
private int steps = 0;
private boolean complete = false;
class Node<T extends Comparable<T>> implements Comparable<Node<T>> {
String label;
T value;
Node(String label, T value) {
this.label = label;
this.value = value;
}
@Override
public String toString() {
return label + ": " + value.toString();
}
@Override
public int compareTo(Node<T> other) {
return value.compareTo(other.value);
}
}
// g represents all potential orders; starts as complete graph
private ListenableGraph<Node<Integer>, DefaultEdge> g;
// g1 represents all actual orders; starts with no edges
private SimpleDirectedGraph<Node<Integer>, DefaultEdge> g1;
private JGraphXAdapter<Node<Integer>, DefaultEdge> jgxAdapter;
@SuppressWarnings("unchecked")
Node<Integer>[] vertexArray = new Node[ARRAY_SIZE];
List<Set<Node<Integer>>> connectedComponentsOfG;
HashMap<Node<Integer>,com.mxgraph.model.mxICell> vertexToCellMap;
HashMap<DefaultEdge,com.mxgraph.model.mxICell> edgeToCellMap;
// sort sets in descending order by number of elements
public class SetComparator implements Comparator<Set<Node<Integer>>> {
@Override
public int compare(Set<Node<Integer>> s1, Set<Node<Integer>> s2) {
return s2.size() - s1.size();
}
}
TransitiveClosure transitiveClosure = TransitiveClosure.INSTANCE;
public enum SortType { RANDOM_BIBD, PIVOT_LINEAR, PIVOT_QUADRATIC, PIVOT_RATIO };
SortType sortType = SortType.PIVOT_RATIO;
public FiveSortPanel() {
Dimension size = new Dimension(600,600);
setPreferredSize(size);
setLayout(new GridBagLayout());
restart();
}
public int getSteps() {
return steps;
}
public boolean isComplete() {
return complete;
}
private void updateConnectedComponents() {
@SuppressWarnings("unchecked")
StrongConnectivityInspector<Node<Integer>,DefaultEdge> sci
= new StrongConnectivityInspector<Node<Integer>,DefaultEdge>(
(DirectedGraph<Node<Integer>, DefaultEdge>) g);
connectedComponentsOfG = sci.stronglyConnectedSets();
Collections.sort(connectedComponentsOfG, new SetComparator());
}
public void step() {
if (!complete) {
chooseFiveAndSort();
++steps;
}
updateConnectedComponents();
complete = true;
for (Set<Node<Integer>> s : connectedComponentsOfG) {
if (s.size() > 1) {
complete = false;
}
}
}
public void restart() {
removeAll();
steps = 0;
complete = false;
if (sortType == SortType.RANDOM_BIBD) {
shuffleBIBD();
}
g = new ListenableDirectedGraph<Node<Integer>, DefaultEdge>(DefaultEdge.class);
g1 = new SimpleDirectedGraph<Node<Integer>, DefaultEdge>(DefaultEdge.class);
jgxAdapter = new JGraphXAdapter<Node<Integer>, DefaultEdge>(g);
vertexToCellMap = jgxAdapter.getVertexToCellMap();
edgeToCellMap = jgxAdapter.getEdgeToCellMap();
jgxAdapter.getStylesheet().getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, "1");
add(new mxGraphComponent(jgxAdapter));
ArrayList<Integer> permutation = new ArrayList<Integer>();
for (int i = 0; i < ARRAY_SIZE; ++i) {
permutation.add(i);
}
Collections.shuffle(permutation);
@SuppressWarnings("unchecked")
Node<Integer>[] n = new Node[ARRAY_SIZE];
for (int i = 0; i < ARRAY_SIZE; ++i) {
n[i] = new Node<Integer>(ALPHABET.substring(i, i+1), permutation.get(i));
vertexArray[i] = n[i];
g.addVertex(n[i]);
g1.addVertex(n[i]);
for (int j = 0; j < i; ++j) {
g.addEdge(n[i], n[j]);
g.addEdge(n[j], n[i]);
}
}
updateConnectedComponents();
mxCircleLayout layout = new mxCircleLayout(jgxAdapter);
layout.execute(jgxAdapter.getDefaultParent());
validate();
repaint();
}
private void chooseFiveAndSort() {
Node<Integer>[] fiveNodes = chooseFive();
for (int i = 0; i < fiveNodes.length-1; ++i) {
g1.addEdge(fiveNodes[i],fiveNodes[i+1]);
}
transitiveClosure.closeSimpleDirectedGraph(g1);
List<Object> edgeCellList = new ArrayList<Object>();
for (int i = 0; i < fiveNodes.length-1; ++i) {
List<Node<Integer>> predList = Graphs.predecessorListOf(g1,fiveNodes[i]);
predList.add(fiveNodes[i]);
List<Node<Integer>> succList = Graphs.successorListOf(g1,fiveNodes[i+1]);
succList.add(fiveNodes[i+1]);
for (Node<Integer> np : predList) {
for (Node<Integer> ns : succList) {
g.removeEdge(ns,np);
edgeCellList.add((Object)(edgeToCellMap.get(g.getEdge(np, ns))));
}
}
}
if (edgeCellList != null) {
jgxAdapter.setCellStyle(STROKE_YELLOW, edgeCellList.toArray());
}
}
private void shuffleBIBD() {
List<Integer[]> BIBDList = (List<Integer[]>) Arrays.asList(BIBD);
Collections.shuffle(BIBDList);
BIBD = BIBDList.toArray(new Integer[0][0]);
}
private Node<Integer>[] chooseFiveRandomBIBD() {
@SuppressWarnings("unchecked")
Node<Integer>[] nodeArray = new Node[SORT_SIZE];
Integer[] indexArray = BIBD[steps];
for (int i = 0; i < SORT_SIZE; ++i) {
nodeArray[i] = vertexArray[indexArray[i]];
}
Arrays.sort(nodeArray);
return nodeArray;
}
private Node<Integer>[] chooseFive() {
switch (sortType) {
case RANDOM_BIBD:
return chooseFiveRandomBIBD();
case PIVOT_LINEAR:
return chooseFivePivotLinear();
case PIVOT_QUADRATIC:
return chooseFivePivotQuadratic();
case PIVOT_RATIO:
return chooseFivePivotRatio();
default:
System.err.println("Internal error: unknown sorting method");
System.exit(1);
}
return null; // inaccessible
}
private Node<Integer>[] chooseFivePivotLinear() {
@SuppressWarnings("unchecked")
Node<Integer>[] nodeArray = new Node[SORT_SIZE];
@SuppressWarnings("unchecked")
Set<Node<Integer>> largestSCC =
(Set<Node<Integer>>) (((HashSet<Node<Integer>>) connectedComponentsOfG.get(0)).clone());
int s = largestSCC.size();
if (s >= 5) {
Node<Integer> pivot = largestSCC.iterator().next();
int pivotDegree = g1.inDegreeOf(pivot) + g1.outDegreeOf(pivot);
int pivotSymmetry = g1.inDegreeOf(pivot) - g1.outDegreeOf(pivot);
for (Node<Integer> n : largestSCC) {
int nDegree = g1.inDegreeOf(n) + g1.outDegreeOf(n);
int nSymmetry = g1.inDegreeOf(n) - g1.outDegreeOf(n);
if (nDegree >= pivotDegree) {
if (Math.abs(nSymmetry) < Math.abs(pivotSymmetry)) {
pivot = n;
pivotDegree = nDegree;
pivotSymmetry = nSymmetry;
}
}
}
int chosen = 0;
nodeArray[chosen++] = pivot;
largestSCC.remove(pivot);
int desiredConnections = 0;
while (chosen < SORT_SIZE) {
Iterator<Node<Integer>> iter = largestSCC.iterator();
while (iter.hasNext()) {
Node<Integer> n = iter.next();
int connectionsWithN = 0;
for (Node<Integer> n1 : nodeArray) {
if (g1.containsEdge(n,n1)) ++connectionsWithN;
if (g1.containsEdge(n1,n)) ++connectionsWithN;
}
if (connectionsWithN <= desiredConnections) {
nodeArray[chosen++] = n;
iter.remove();
if (chosen == SORT_SIZE) break;
}
}
++desiredConnections;
}
} else if (s == 4) { // take all 4 elements and 1 from elsewhere (which doesn't help)
int chosen = 0;
for (Node<Integer> n : largestSCC) {
nodeArray[chosen++] = n;
}
nodeArray[chosen++] = connectedComponentsOfG.get(1).iterator().next();
} else if (s == 3) { // take all 3 elements and find 2 from elsewhere
int chosen = 0;
for (Node<Integer> n : largestSCC) {
nodeArray[chosen++] = n;
}
for (Set<Node<Integer>> scc : connectedComponentsOfG) { // prefer size 2 component
if (scc.size() == 2) {
for (Node<Integer> n : scc) {
nodeArray[chosen++] = n;
}
break;
}
}
if (chosen < SORT_SIZE) { // no size 2 component found
if (connectedComponentsOfG.get(1).size() == 3) { // take 2
Iterator<Node<Integer>> iter = connectedComponentsOfG.get(1).iterator();
nodeArray[chosen++] = iter.next();
nodeArray[chosen++] = iter.next();
} else {
nodeArray[chosen++] = connectedComponentsOfG.get(1).iterator().next();
nodeArray[chosen++] = connectedComponentsOfG.get(2).iterator().next();
}
}
} else if (s == 2) { // take both; all from next SCC, and 1 from next, and 1 more if nec.
int chosen = 0;
for (Node<Integer> n : largestSCC) {
nodeArray[chosen++] = n;
}
for (Node<Integer> n : connectedComponentsOfG.get(1)) {
nodeArray[chosen++] = n;
}
nodeArray[chosen++] = connectedComponentsOfG.get(2).iterator().next();
if (connectedComponentsOfG.get(1).size() == 1) {
nodeArray[chosen++] = connectedComponentsOfG.get(3).iterator().next();
}
} else if (s == 1) {
System.err.println("Internal Error: should have been complete by now");
System.exit(1);
}
Arrays.sort(nodeArray);
return nodeArray;
}
private Node<Integer>[] chooseFivePivotQuadratic() {
@SuppressWarnings("unchecked")
Node<Integer>[] nodeArray = new Node[SORT_SIZE];
@SuppressWarnings("unchecked")
Set<Node<Integer>> largestSCC =
(Set<Node<Integer>>) (((HashSet<Node<Integer>>) connectedComponentsOfG.get(0)).clone());
int s = largestSCC.size();
if (s >= 5) {
Node<Integer> pivot = largestSCC.iterator().next();
double pivotDegree = 7*g1.inDegreeOf(pivot) * g1.outDegreeOf(pivot)
- (g1.inDegreeOf(pivot)) * (g1.inDegreeOf(pivot))
- (g1.outDegreeOf(pivot)) - (g1.outDegreeOf(pivot));
for (Node<Integer> n : largestSCC) {
double nDegree = 7*g1.inDegreeOf(n) * g1.outDegreeOf(n)
- (g1.inDegreeOf(n)) * (g1.inDegreeOf(n))
- (g1.outDegreeOf(n)) - (g1.outDegreeOf(n));
if (nDegree >= pivotDegree) {
pivot = n;
pivotDegree = nDegree;
}
}
int chosen = 0;
nodeArray[chosen++] = pivot;
largestSCC.remove(pivot);
int desiredConnections = 0;
while (chosen < SORT_SIZE) {
Iterator<Node<Integer>> iter = largestSCC.iterator();
while (iter.hasNext()) {
Node<Integer> n = iter.next();
int connectionsWithN = 0;
for (Node<Integer> n1 : nodeArray) {
if (g1.containsEdge(n,n1)) ++connectionsWithN;
if (g1.containsEdge(n1,n)) ++connectionsWithN;
}
if (connectionsWithN <= desiredConnections) {
nodeArray[chosen++] = n;
iter.remove();
if (chosen == SORT_SIZE) break;
}
}
++desiredConnections;
}
} else if (s == 4) { // take all 4 elements and 1 from elsewhere (which doesn't help)
int chosen = 0;
for (Node<Integer> n : largestSCC) {
nodeArray[chosen++] = n;
}
nodeArray[chosen++] = connectedComponentsOfG.get(1).iterator().next();
} else if (s == 3) { // take all 3 elements and find 2 from elsewhere
int chosen = 0;
for (Node<Integer> n : largestSCC) {
nodeArray[chosen++] = n;
}
for (Set<Node<Integer>> scc : connectedComponentsOfG) { // prefer size 2 component
if (scc.size() == 2) {
for (Node<Integer> n : scc) {
nodeArray[chosen++] = n;
}
break;
}
}
if (chosen < SORT_SIZE) { // no size 2 component found
if (connectedComponentsOfG.get(1).size() == 3) { // take 2
Iterator<Node<Integer>> iter = connectedComponentsOfG.get(1).iterator();
nodeArray[chosen++] = iter.next();
nodeArray[chosen++] = iter.next();
} else {
nodeArray[chosen++] = connectedComponentsOfG.get(1).iterator().next();
nodeArray[chosen++] = connectedComponentsOfG.get(2).iterator().next();
}
}
} else if (s == 2) { // take both; all from next SCC, and 1 from next, and 1 more if nec.
int chosen = 0;
for (Node<Integer> n : largestSCC) {
nodeArray[chosen++] = n;
}
for (Node<Integer> n : connectedComponentsOfG.get(1)) {
nodeArray[chosen++] = n;
}
nodeArray[chosen++] = connectedComponentsOfG.get(2).iterator().next();
if (connectedComponentsOfG.get(1).size() == 1) {
nodeArray[chosen++] = connectedComponentsOfG.get(3).iterator().next();
}
} else if (s == 1) {
System.err.println("Internal Error: should have been complete by now");
System.exit(1);
}
Arrays.sort(nodeArray);
return nodeArray;
}
private Node<Integer>[] chooseFivePivotRatio() {
@SuppressWarnings("unchecked")
Node<Integer>[] nodeArray = new Node[SORT_SIZE];
@SuppressWarnings("unchecked")
Set<Node<Integer>> largestSCC =
(Set<Node<Integer>>) (((HashSet<Node<Integer>>) connectedComponentsOfG.get(0)).clone());
int s = largestSCC.size();
if (s >= 5) {
Node<Integer> pivot = largestSCC.iterator().next();
int pivotMinInOut = Math.min(g1.inDegreeOf(pivot),g1.outDegreeOf(pivot));
int pivotMaxInOut = Math.max(g1.inDegreeOf(pivot),g1.outDegreeOf(pivot));
double pivotRatio = (pivotMaxInOut == 0) ? 0 : pivotMinInOut/((double)pivotMaxInOut);
for (Node<Integer> n : largestSCC) {
int nMinInOut = Math.min(g1.inDegreeOf(n),g1.outDegreeOf(n));
int nMaxInOut = Math.max(g1.inDegreeOf(n),g1.outDegreeOf(n));
double nRatio = (nMaxInOut == 0) ? 0 : nMinInOut/((double)nMaxInOut);
if (nRatio > pivotRatio) {
pivot = n;
pivotRatio = nRatio;
}
}
int chosen = 0;
nodeArray[chosen++] = pivot;
largestSCC.remove(pivot);
int desiredConnections = 0;
while (chosen < SORT_SIZE) {
Iterator<Node<Integer>> iter = largestSCC.iterator();
while (iter.hasNext()) {
Node<Integer> n = iter.next();
int connectionsWithN = 0;
for (Node<Integer> n1 : nodeArray) {
if (g1.containsEdge(n,n1)) ++connectionsWithN;
if (g1.containsEdge(n1,n)) ++connectionsWithN;
}
if (connectionsWithN <= desiredConnections) {
nodeArray[chosen++] = n;
iter.remove();
if (chosen == SORT_SIZE) break;
}
}
++desiredConnections;
}
} else if (s == 4) { // take all 4 elements and 1 from elsewhere (which doesn't help)
int chosen = 0;
for (Node<Integer> n : largestSCC) {
nodeArray[chosen++] = n;
}
nodeArray[chosen++] = connectedComponentsOfG.get(1).iterator().next();
} else if (s == 3) { // take all 3 elements and find 2 from elsewhere
int chosen = 0;
for (Node<Integer> n : largestSCC) {
nodeArray[chosen++] = n;
}
for (Set<Node<Integer>> scc : connectedComponentsOfG) { // prefer size 2 component
if (scc.size() == 2) {
for (Node<Integer> n : scc) {
nodeArray[chosen++] = n;
}
break;
}
}
if (chosen < SORT_SIZE) { // no size 2 component found
if (connectedComponentsOfG.get(1).size() == 3) { // take 2
Iterator<Node<Integer>> iter = connectedComponentsOfG.get(1).iterator();
nodeArray[chosen++] = iter.next();
nodeArray[chosen++] = iter.next();
} else {
nodeArray[chosen++] = connectedComponentsOfG.get(1).iterator().next();
nodeArray[chosen++] = connectedComponentsOfG.get(2).iterator().next();
}
}
} else if (s == 2) { // take both; all from next SCC, and 1 from next, and 1 more if nec.
int chosen = 0;
for (Node<Integer> n : largestSCC) {
nodeArray[chosen++] = n;
}
for (Node<Integer> n : connectedComponentsOfG.get(1)) {
nodeArray[chosen++] = n;
}
nodeArray[chosen++] = connectedComponentsOfG.get(2).iterator().next();
if (connectedComponentsOfG.get(1).size() == 1) {
nodeArray[chosen++] = connectedComponentsOfG.get(3).iterator().next();
}
} else if (s == 1) {
System.err.println("Internal Error: should have been complete by now");
System.exit(1);
}
Arrays.sort(nodeArray);
return nodeArray;
}
}
答案 3 :(得分:1)
这个问题的一个有趣的角度是使用“组合块设计”的想法。我们希望我们的每一种都能尽可能多地给我们提供信息,因此我们不希望在两种不同的种类中使用相同的元素对。这实际上是可以实现的:我们可以使用称为“平衡不完全块设计”(BIBD)的组合结构。我们正在寻找一个(25,5,1)-BIBD,这意味着有25个元素(25),每次被5个阻塞(5),这样每对元素恰好出现在一个块(1)中。 / p>
这种区块设计已被广泛探索。事实证明,有一个(25,5,1)-BIBD。在例如http://arxiv.org/ftp/arxiv/papers/0909/0909.3533.pdf第8页中给出了明确的结构。
{(1,2,3,4,5) (6,7,8,9,10) (11,12,13,14,15) (16,17,18,19,20) (21,22,23,24,25)
(1,6,11,16,21) (2,7,12,17,21) (3,8,13,18,21) (4,9,14,19,21) (5,10,15,20,21)
(2,8,14,20,22) (3,10,11,19,22) (5,9,12,16,22) (1,7,15,18,22) (4,6,13,17,22)
(3,9,15,17,23) (5,6,14,18,23) (4,7,11,20,23) (2,10,13,16,23) (1,8,12,19,23)
(4,10,12,18,24) (1,9,13,20,24) (2,6,15,19,24) (5,8,11,17,24) (3,7,14,16,24)
(5,7,13,19,25) (4,8,15,16,25) (1,10,14,17,25) (3,6,12,20,25) (2,9,11,18,25)}
Sage也可用于构建BIBD。
该块设计有30个块,因此对于这个问题来说远非最佳。但也许它可以与传递性相结合,为问题设计更快的算法。
<强>更新强>
事实证明,除了在解决方案中的步数(30,BIBD的大小)上设置上限之外,我的建议在此问题中没有多大用处。为了衡量它的性能,我写了一些“测试床”软件(见下文),它可以直观地显示排序的进度。
我用两个图表示数据的排序状态:g,25个项目之间所有潜在关系的图表,它们作为25个项目的完整有向图开始,g1,所有已知关系的图表在25个项目中。 g和g1有明显的关系,因此跟踪两个图表显然是多余的,但是可以很容易地从g和g1中提取不同类型的信息,这就是我为什么要跟踪它们的原因。
g以600条边开始,每条25 * 24方向边在两个项之间。当g没有非平凡的(即,大小大于1)强连通分量时,我们就完成了,在这种情况下,g可以明确地被转发以给出正确的排序。当g中恰好有300个边缘时会发生这种情况。类似地,g1从没有边开始,当g1中出现与g相同的300个边时,我们就完成了。
挑选5个项目,然后立即对它们进行排序,为g添加最多5 + 4 + 3 + 2 + 1 = 15个新边(并从g1中删除相同数量的边)。我说“up to”因为如果这些边缘中的任何一个已经在g中,它们就不会被添加到计数中。所以,如果我们已经知道A - &gt; B并没有关于A,B,C,D,E中任何其他对之间的关系,然后对这五个进行排序只能得到14个新的边缘。
另一方面,我们通常可以通过利用传递性来获得更多箭头。如果我们知道B - > F,并且排序告诉我们A - &gt; B,我们可以推断出A - &gt; F,这是g中的额外箭头。通过传递性找到的所有附加箭头的集合可以通过找到g的“传递闭包”来获得,其中新的箭头作为排序的结果被添加。可以很容易地找到对g1的相应影响。
在我下面的软件中,我给出了图形g1的图像,图形开始时为600个有向边缘,图示为300个蓝色边缘,每边有箭头。传递闭合后的每个分类步骤将用单面黄色箭头替换一些双面蓝色箭头。我们知道,当所有蓝色箭头消失时我们已经完成了;等效地,当g1没有非平凡的强连通分量时。
在下面的软件中,我通过从之前给出的BIBD中挑选一个随机未使用的块来选择要排序的5个项目,然后将该块标记为已使用。通常需要所有30个块以这种方式对25个项目进行排序。从可视化中可以看出,该过程开始时已经足够好,但是从第20步到第30步没有明显的加速。我的结论是这个问题的最快解决方案必须是自适应的,根据先前的结果挑选5个项目进行排序
以这种方式看问题给了我一些新的想法,我可以在另一个问题的答案中探讨。与此同时,这里是我使用的程序的两个Java类。
GUI框架:
package org.jgrapht.demo;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class FiveSort extends JFrame {
private static final long serialVersionUID = 1L;
private Font smallFont = new Font(Font.DIALOG, Font.PLAIN, 12);
private Font largeFont = new Font(Font.DIALOG, Font.PLAIN, 36);
private JLabel stepsLabel = new JLabel("0");
private JLabel maxLabel = new JLabel("0");
private JLabel averageLabel = new JLabel("");
private int rounds = 0;
private int totalSteps = 0;
private double averageSteps = 0;
private int maxSteps = 0;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new FiveSort();
}
});
}
public FiveSort() {
initGUI();
setLocationRelativeTo(null);
setTitle("Five Sort");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public void initGUI() {
try {
// UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName() );
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
} catch (Exception e) {
e.printStackTrace();
}
// title label
JLabel titleLabel = new JLabel("Five Sort");
titleLabel.setFont(largeFont);
titleLabel.setBackground(Color.BLACK);
titleLabel.setForeground(Color.WHITE);
titleLabel.setOpaque(true);
titleLabel.setHorizontalAlignment(JLabel.CENTER);
add(titleLabel,BorderLayout.PAGE_START);
// main panel
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel,BoxLayout.Y_AXIS));
add(mainPanel);
// graph panel
FiveSortPanel graphPanel = new FiveSortPanel();
mainPanel.add(graphPanel,BorderLayout.CENTER);
// stats panel
JPanel statsPanel = new JPanel();
statsPanel.setBackground(Color.BLACK);
mainPanel.add(statsPanel);
JLabel stepsTitleLabel = new JLabel("Current Steps: ");
stepsTitleLabel.setFont(smallFont);
stepsTitleLabel.setForeground(Color.WHITE);
statsPanel.add(stepsTitleLabel);
stepsLabel.setFont(largeFont);
stepsLabel.setForeground(Color.WHITE);
stepsLabel.setText("" + graphPanel.getSteps());
statsPanel.add(stepsLabel);
JLabel maxTitleLabel = new JLabel("Max Steps: ");
maxTitleLabel.setFont(smallFont);
maxTitleLabel.setForeground(Color.WHITE);
statsPanel.add(maxTitleLabel);
maxLabel.setFont(largeFont);
maxLabel.setForeground(Color.WHITE);
maxLabel.setText("" + maxSteps);
statsPanel.add(maxLabel);
JLabel averageTitleLabel = new JLabel("Avg Steps: ");
averageTitleLabel.setFont(smallFont);
averageTitleLabel.setForeground(Color.WHITE);
statsPanel.add(averageTitleLabel);
averageLabel.setFont(largeFont);
averageLabel.setForeground(Color.WHITE);
averageLabel.setText("");
statsPanel.add(averageLabel);
// button panel
JPanel buttonPanel = new JPanel();
buttonPanel.setBackground(Color.BLACK);
add(buttonPanel,BorderLayout.PAGE_END);
Button newButton = new Button("Step");
newButton.setFocusable(false);
newButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!graphPanel.isComplete()) {
graphPanel.step();
stepsLabel.setText("" + graphPanel.getSteps());
}
}
});
buttonPanel.add(newButton);
Button restartButton = new Button("Restart");
restartButton.setFocusable(false);
restartButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (graphPanel.isComplete()) {
++rounds;
totalSteps += graphPanel.getSteps();
averageSteps = ((int)(totalSteps / (double)rounds * 10))/10.0;
maxSteps = Math.max(maxSteps, graphPanel.getSteps());
}
graphPanel.restart();
stepsLabel.setText("" + graphPanel.getSteps());
maxLabel.setText("" + maxSteps);
averageLabel.setText("" + averageSteps);
}
});
buttonPanel.add(restartButton);
}
}
图形操作例程(需要jgrapht-ext-0.9.1-uber.jar,可从JGraphT网站免费获得):
package org.jgrapht.demo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import javax.swing.JPanel;
import org.jgrapht.DirectedGraph;
import org.jgrapht.Graphs;
import org.jgrapht.ListenableGraph;
import org.jgrapht.alg.StrongConnectivityInspector;
import org.jgrapht.alg.TransitiveClosure;
import org.jgrapht.ext.JGraphXAdapter;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.ListenableDirectedGraph;
import org.jgrapht.graph.SimpleDirectedGraph;
import com.mxgraph.layout.mxCircleLayout;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
public class FiveSortPanel extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ARRAY_SIZE = 25;
private static final int SORT_SIZE = 5;
private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String STROKE_YELLOW = "strokeColor=#CCCC00";
private Integer[][] BIBD = {
{1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15}, {16,17,18,19,20}, {21,22,23,24,0},
{1,6,11,16,21}, {2,7,12,17,21}, {3,8,13,18,21}, {4,9,14,19,21}, {5,10,15,20,21},
{2,8,14,20,22}, {3,10,11,19,22}, {5,9,12,16,22}, {1,7,15,18,22}, {4,6,13,17,22},
{3,9,15,17,23}, {5,6,14,18,23}, {4,7,11,20,23}, {2,10,13,16,23}, {1,8,12,19,23},
{4,10,12,18,24}, {1,9,13,20,24}, {2,6,15,19,24}, {5,8,11,17,24}, {3,7,14,16,24},
{5,7,13,19,0}, {4,8,15,16,0}, {1,10,14,17,0}, {3,6,12,20,0}, {2,9,11,18,0}
};
private int steps = 0;
private boolean complete = false;
class Node<T extends Comparable<T>> implements Comparable<Node<T>> {
String label;
T value;
Node(String label, T value) {
this.label = label;
this.value = value;
}
@Override
public String toString() {
return label + ": " + value.toString();
}
@Override
public int compareTo(Node<T> other) {
return value.compareTo(other.value);
}
}
// g represents all potential orders; starts as complete graph
private ListenableGraph<Node<Integer>, DefaultEdge> g;
// g1 represents all actual orders; starts with no edges
private SimpleDirectedGraph<Node<Integer>, DefaultEdge> g1;
private JGraphXAdapter<Node<Integer>, DefaultEdge> jgxAdapter;
@SuppressWarnings("unchecked")
Node<Integer>[] vertexArray = new Node[ARRAY_SIZE];
List<Set<Node<Integer>>> connectedComponentsOfG;
HashMap<Node<Integer>,com.mxgraph.model.mxICell> vertexToCellMap;
HashMap<DefaultEdge,com.mxgraph.model.mxICell> edgeToCellMap;
// sort sets in descending order by number of elements
public class SetComparator implements Comparator<Set<Node<Integer>>> {
@Override
public int compare(Set<Node<Integer>> s1, Set<Node<Integer>> s2) {
return s2.size() - s1.size();
}
}
TransitiveClosure transitiveClosure = TransitiveClosure.INSTANCE;
public FiveSortPanel() {
restart();
}
public int getSteps() {
return steps;
}
public boolean isComplete() {
return complete;
}
private void updateConnectedComponents() {
@SuppressWarnings("unchecked")
StrongConnectivityInspector<Node<Integer>,DefaultEdge> sci
= new StrongConnectivityInspector<Node<Integer>,DefaultEdge>(
(DirectedGraph<Node<Integer>, DefaultEdge>) g);
connectedComponentsOfG = sci.stronglyConnectedSets();
Collections.sort(connectedComponentsOfG, new SetComparator());
}
public void step() {
if (!complete) {
chooseFiveAndSort();
++steps;
}
updateConnectedComponents();
complete = true;
for (Set<Node<Integer>> s : connectedComponentsOfG) {
if (s.size() > 1) {
complete = false;
}
}
}
public void restart() {
removeAll();
steps = 0;
complete = false;
shuffleBIBD();
g = new ListenableDirectedGraph<Node<Integer>, DefaultEdge>(DefaultEdge.class);
g1 = new SimpleDirectedGraph<Node<Integer>, DefaultEdge>(DefaultEdge.class);
jgxAdapter = new JGraphXAdapter<Node<Integer>, DefaultEdge>(g);
vertexToCellMap = jgxAdapter.getVertexToCellMap();
edgeToCellMap = jgxAdapter.getEdgeToCellMap();
jgxAdapter.getStylesheet().getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, "1");
add(new mxGraphComponent(jgxAdapter));
ArrayList<Integer> permutation = new ArrayList<Integer>();
for (int i = 0; i < ARRAY_SIZE; ++i) {
permutation.add(i);
}
Collections.shuffle(permutation);
@SuppressWarnings("unchecked")
Node<Integer>[] n = new Node[ARRAY_SIZE];
for (int i = 0; i < ARRAY_SIZE; ++i) {
n[i] = new Node<Integer>(ALPHABET.substring(i, i+1), permutation.get(i));
vertexArray[i] = n[i];
g.addVertex(n[i]);
g1.addVertex(n[i]);
for (int j = 0; j < i; ++j) {
g.addEdge(n[i], n[j]);
g.addEdge(n[j], n[i]);
}
}
updateConnectedComponents();
mxCircleLayout layout = new mxCircleLayout(jgxAdapter);
layout.execute(jgxAdapter.getDefaultParent());
//repaint();
}
private void chooseFiveAndSort() {
Node<Integer>[] fiveNodes = chooseFive();
for (int i = 0; i < fiveNodes.length-1; ++i) {
g1.addEdge(fiveNodes[i],fiveNodes[i+1]);
}
transitiveClosure.closeSimpleDirectedGraph(g1);
List<Object> edgeCellList = new ArrayList<Object>();
for (int i = 0; i < fiveNodes.length-1; ++i) {
List<Node<Integer>> predList = Graphs.predecessorListOf(g1,fiveNodes[i]);
predList.add(fiveNodes[i]);
List<Node<Integer>> succList = Graphs.successorListOf(g1,fiveNodes[i+1]);
succList.add(fiveNodes[i+1]);
for (Node<Integer> np : predList) {
for (Node<Integer> ns : succList) {
g.removeEdge(ns,np);
edgeCellList.add((Object)(edgeToCellMap.get(g.getEdge(np, ns))));
}
}
}
jgxAdapter.setCellStyle(STROKE_YELLOW, edgeCellList.toArray());
}
private Node<Integer>[] chooseFive() {
return chooseFiveRandomBIBD();
}
private void shuffleBIBD() {
List<Integer[]> BIBDList = (List<Integer[]>) Arrays.asList(BIBD);
Collections.shuffle(BIBDList);
BIBD = BIBDList.toArray(new Integer[0][0]);
}
private Node<Integer>[] chooseFiveRandomBIBD() {
Integer[] indexArray = BIBD[steps];
@SuppressWarnings("unchecked")
Node<Integer>[] nodeArray = new Node[SORT_SIZE];
for (int i = 0; i < SORT_SIZE; ++i) {
nodeArray[i] = vertexArray[indexArray[i]];
}
Arrays.sort(nodeArray);
return nodeArray;
}
}
答案 4 :(得分:0)
让我们将5组中的所有元素分成5个元素,从每个组中删除最后2个元素(这些数字少于3个数字,并且肯定不能成为解决方案的候选者)。我们多次执行相同的程序,直到我们得到一个具有5个元素的组,这些元素最后一次排序并将其前三个元素作为解决方案。
以下是排序操作的计数
5 groups by 5 elements - 5 sort
25 - 10 = 15 elements in 3 groups - 3 sort
15 - 6 = 9 elements in 2 groups ( one with 4 elements, the other with 5 elements) - 1 sort
4 + 3 = 7 elements in 2 groups ( one with 5 and one with 2 element) 1 sort
3 + 2 = 5 elements 1 sort
Total count: 5 + 3 + 1 + 1 + 1 = 11 sort