-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
// /// Kernighan–Lin 알고리즘을 이용하여 그래프를 두 그룹으로 분할한다.
// ///
// /// # 인수
// /// - `graph`: 노드에 ()를, 엣지에 i32 가중치를 가지는 무방향 그래프.
// ///
// /// # 반환값
// /// 두 개의 HashSet<NodeIndex>로, 각각 그룹 A와 그룹 B의 노드들을 나타낸다.
// pub fn kernighan_lin(graph: &UnGraph<(), i32>) -> (HashSet<NodeIndex>, HashSet<NodeIndex>) {
// let n = graph.node_count();
// if n == 0 {
// return (HashSet::new(), HashSet::new());
// }
// // 초기 분할: 노드들을 정렬한 후 앞쪽 절반은 그룹 A, 나머지는 그룹 B로 배정.
// let mut partition = vec![false; n]; // true: 그룹 A, false: 그룹 B
// let mut nodes: Vec<NodeIndex> = graph.node_indices().collect();
// nodes.sort_by_key(|n| n.index());
// let half = nodes.len() / 2;
// for (i, &node) in nodes.iter().enumerate() {
// if i < half {
// partition[node.index()] = true; // 그룹 A
// } else {
// partition[node.index()] = false; // 그룹 B
// }
// }
// let mut improvement = true;
// // 반복: swap을 통해 절단 비용을 줄일 수 있으면 계속 진행
// while improvement {
// improvement = false;
// // 각 노드의 D값 계산: D(v) = (v와 다른 그룹 노드와의 연결 가중치 합) - (v와 같은 그룹 노드와의 연결 가중치 합)
// let mut d = vec![0; n];
// for node in graph.node_indices() {
// let idx = node.index();
// let mut internal = 0;
// let mut external = 0;
// for edge in graph.edges(node) {
// let neighbor = edge.target();
// let weight = *edge.weight();
// if partition[neighbor.index()] == partition[idx] {
// internal += weight;
// } else {
// external += weight;
// }
// }
// d[idx] = external - internal;
// }
// // swap 과정에서 이미 선택한 노드를 기록하기 위한 locked 배열
// let mut locked = vec![false; n];
// // 선택한 swap 쌍과 각 쌍의 gain 값을 저장하는 벡터
// let mut swap_pairs: Vec<(NodeIndex, NodeIndex, i32)> = Vec::new();
// let mut gains: Vec<i32> = Vec::new();
// // 한 번의 패스에서 교환 가능한 최대 쌍의 수는 min(그룹 A의 노드 수, 그룹 B의 노드 수)
// let count_a = partition.iter().filter(|&&p| p).count();
// let count_b = n - count_a;
// let max_possible_swaps = std::cmp::min(count_a, count_b);
// // 아직 locked되지 않은 그룹 A와 그룹 B의 노드 쌍에 대해 최대 gain을 주는 swap 쌍을 반복적으로 선택
// for _ in 0..max_possible_swaps {
// let mut best_gain = std::i32::MIN;
// let mut best_pair: Option<(NodeIndex, NodeIndex)> = None;
// // 그룹 A의 unlocked 노드 i와 그룹 B의 unlocked 노드 j에 대해 gain을 계산
// for &i in &nodes {
// if locked[i.index()] || !partition[i.index()] {
// continue; // i는 그룹 A에 있어야 함.
// }
// for &j in &nodes {
// if locked[j.index()] || partition[j.index()] {
// continue; // j는 그룹 B에 있어야 함.
// }
// // i와 j 사이에 edge가 있으면 그 가중치를, 없으면 0으로 취급
// let weight = if let Some(edge) = graph.find_edge(i, j) {
// *graph.edge_weight(edge).unwrap()
// } else {
// 0
// };
// let gain = d[i.index()] + d[j.index()] - 2 * weight;
// if gain > best_gain {
// best_gain = gain;
// best_pair = Some((i, j));
// }
// }
// }
// // 더 이상 선택할 쌍이 없으면 break
// if best_pair.is_none() {
// break;
// }
// let (a, b) = best_pair.unwrap();
// swap_pairs.push((a, b, best_gain));
// if gains.is_empty() {
// gains.push(best_gain);
// } else {
// gains.push(gains.last().unwrap() + best_gain);
// }
// // 선택된 a, b는 locked 처리
// locked[a.index()] = true;
// locked[b.index()] = true;
// // 아직 locked되지 않은 각 노드 k에 대해 D값 업데이트:
// // - k가 그룹 A에 있으면: D(k) = D(k) + 2*(w(k, a) - w(k, b))
// // - k가 그룹 B에 있으면: D(k) = D(k) + 2*(w(k, b) - w(k, a))
// for &k in &nodes {
// if locked[k.index()] {
// continue;
// }
// let w_ka = if let Some(edge) = graph.find_edge(k, a) {
// *graph.edge_weight(edge).unwrap()
// } else {
// 0
// };
// let w_kb = if let Some(edge) = graph.find_edge(k, b) {
// *graph.edge_weight(edge).unwrap()
// } else {
// 0
// };
// if partition[k.index()] {
// // k는 그룹 A에 있음
// d[k.index()] += 2 * (w_ka - w_kb);
// } else {
// // k는 그룹 B에 있음
// d[k.index()] += 2 * (w_kb - w_ka);
// }
// }
// }
// // 각 swap 쌍을 순서대로 적용했을 때의 누적 gain의 최대값과 그때의 쌍 개수를 찾음.
// let mut max_gain = std::i32::MIN;
// let mut k_max = None;
// for (k, &gain_sum) in gains.iter().enumerate() {
// if gain_sum > max_gain {
// max_gain = gain_sum;
// k_max = Some(k);
// }
// }
// // 누적 gain이 양수이면, 실제로 swap 쌍들을 적용.
// if max_gain > 0 {
// improvement = true;
// let num_swaps = k_max.unwrap() + 1;
// for i in 0..num_swaps {
// let (a, b, _) = swap_pairs[i];
// // swap: a는 그룹 A에서 그룹 B로, b는 그룹 B에서 그룹 A로 이동
// partition[a.index()] = false;
// partition[b.index()] = true;
// }
// }
// }
// // 최종 분할 결과를 HashSet으로 생성하여 반환.
// let set_a: HashSet<NodeIndex> = nodes
// .iter()
// .filter(|&&node| partition[node.index()])
// .copied()
// .collect();
// let set_b: HashSet<NodeIndex> = nodes
// .iter()
// .filter(|&&node| !partition[node.index()])
// .copied()
// .collect();
// (set_a, set_b)
// }
// #[cfg(test)]
// mod tests {
// use std::collections::HashSet;
// use petgraph::graph::{NodeIndex, UnGraph};
// use super::*;
// #[test]
// fn test_simple_partition() {
// // 예제: 간단한 무방향 가중치 그래프 생성
// let mut graph = UnGraph::<(), i32>::new_undirected();
// // 노드 추가
// let a = graph.add_node(()); // Node 0
// let b = graph.add_node(()); // Node 1
// let c = graph.add_node(()); // Node 2
// let d = graph.add_node(()); // Node 3
// // 엣지 추가 (가중치 지정)
// graph.add_edge(a, b, 3);
// graph.add_edge(a, c, 1);
// graph.add_edge(b, c, 3);
// graph.add_edge(b, d, 1);
// graph.add_edge(c, d, 2);
// let (set_a, set_b) = kernighan_lin(&graph);
// println!("Group A: {:?}", set_a);
// println!("Group B: {:?}", set_b);
// }
// #[test]
// fn test_empty_graph() {
// let graph: UnGraph<(), i32> = UnGraph::new_undirected();
// let (set_a, set_b) = kernighan_lin(&graph);
// assert!(
// set_a.is_empty(),
// "Group A should be empty for an empty graph."
// );
// assert!(
// set_b.is_empty(),
// "Group B should be empty for an empty graph."
// );
// }
// #[test]
// fn test_two_nodes_graph() {
// let mut graph = UnGraph::<(), i32>::new_undirected();
// let a = graph.add_node(());
// let b = graph.add_node(());
// graph.add_edge(a, b, 5);
// let (set_a, set_b) = kernighan_lin(&graph);
// // 두 노드가 서로 다른 그룹에 속하는지 확인
// assert_eq!(set_a.len() + set_b.len(), 2, "총 노드 수는 2여야 합니다.");
// // 한 노드는 그룹 A, 다른 노드는 그룹 B에 있어야 함
// assert!(
// set_a.contains(&a) ^ set_b.contains(&a),
// "노드 a는 한 그룹에만 속해야 합니다."
// );
// assert!(
// set_a.contains(&b) ^ set_b.contains(&b),
// "노드 b는 한 그룹에만 속해야 합니다."
// );
// }
// #[test]
// fn test_six_nodes_graph() {
// let mut graph = UnGraph::<(), i32>::new_undirected();
// // 노드 추가 (노드가 추가되는 순서대로 인덱스가 0,1,... 할당됨)
// let a = graph.add_node(()); // index 0
// let b = graph.add_node(()); // index 1
// let c = graph.add_node(()); // index 2
// let d = graph.add_node(()); // index 3
// let e = graph.add_node(()); // index 4
// let f = graph.add_node(()); // index 5
// // 그룹 A 내부 (노드 0,1,2): 강한 연결
// graph.add_edge(a, b, 10);
// graph.add_edge(b, c, 10);
// graph.add_edge(a, c, 10);
// // 그룹 B 내부 (노드 3,4,5): 강한 연결
// graph.add_edge(d, e, 10);
// graph.add_edge(e, f, 10);
// graph.add_edge(d, f, 10);
// // 두 그룹 사이의 연결: 약한 연결
// graph.add_edge(c, d, 1);
// graph.add_edge(b, e, 1);
// graph.add_edge(a, d, 1);
// let (set_a, set_b) = kernighan_lin(&graph);
// // 초기 분할에 따라 노드 0,1,2는 그룹 A, 노드 3,4,5는 그룹 B가 되어야 함.
// let expected_a: HashSet<NodeIndex> =
// [NodeIndex::new(0), NodeIndex::new(1), NodeIndex::new(2)]
// .iter()
// .copied()
// .collect();
// let expected_b: HashSet<NodeIndex> =
// [NodeIndex::new(3), NodeIndex::new(4), NodeIndex::new(5)]
// .iter()
// .copied()
// .collect();
// // 분할 결과는 그룹 이름(label)이 뒤바뀔 수 있으므로 두 경우 모두 허용.
// if set_a == expected_a && set_b == expected_b {
// // 올바른 분할
// } else if set_a == expected_b && set_b == expected_a {
// // 올바른 분할 (그룹 레이블이 반대)
// } else {
// panic!(
// "분할 결과가 예상과 다릅니다.\nGroup A: {:?}\nGroup B: {:?}",
// set_a, set_b
// );
// }
// }
// }
// / 단순 노드 구조체
// #[derive(Debug)]
// struct Node;
// /// --- 1. 초기 분할을 인자로 받는 Kernighan–Lin 알고리즘 ---
// ///
// /// 인자로 받은 초기 partition(벡터의 i번째 요소가 true이면 노드 i는 그룹 A)에서 시작하여,
// /// gain이 개선되는 동안 swap을 반복한 후 최종 파티션을 반환한다.
// ///
// /// **주의:** 이 알고리즘은 입력 directed graph의 모든 간선(Outgoing, Incoming)을
// /// 합산하여 undirected하게 처리합니다.
// fn kernighan_lin_with_initial(
// graph: &Graph<Node, usize, Directed>,
// init_partition: &Vec<bool>,
// ) -> Vec<bool> {
// let n = graph.node_count();
// let mut partition = init_partition.clone();
// // 노드들을 인덱스 순서대로 정렬 (안정적인 순서를 위해)
// let mut nodes: Vec<NodeIndex> = graph.node_indices().collect();
// nodes.sort_by_key(|n| n.index());
// let mut improvement = true;
// while improvement {
// improvement = false;
// // 각 노드 v에 대해 D(v) = (외부 연결 가중치 합) - (내부 연결 가중치 합)
// let mut d = vec![0i64; n];
// for (i, &node) in nodes.iter().enumerate() {
// let mut internal = 0i64;
// let mut external = 0i64;
// // Outgoing edge 처리
// for edge in graph.edges_directed(node, petgraph::Direction::Outgoing) {
// let neighbor = edge.target();
// let w = *edge.weight() as i64;
// if partition[neighbor.index()] == partition[node.index()] {
// internal += w;
// } else {
// external += w;
// }
// }
// // Incoming edge 처리
// for edge in graph.edges_directed(node, petgraph::Direction::Incoming) {
// let neighbor = edge.source();
// let w = *edge.weight() as i64;
// if partition[neighbor.index()] == partition[node.index()] {
// internal += w;
// } else {
// external += w;
// }
// }
// d[i] = external - internal;
// }
// // 한 번의 swap 패스에서 이미 사용한 노드를 기록
// let mut locked = vec![false; n];
// // 선택한 swap 쌍과 각 쌍의 gain을 저장 (인덱스는 nodes 벡터 상의 위치)
// let mut swap_pairs: Vec<(usize, usize, i64)> = Vec::new();
// let mut gains: Vec<i64> = Vec::new();
// let count_a = partition.iter().filter(|&&b| b).count();
// let count_b = n - count_a;
// let max_possible_swaps = std::cmp::min(count_a, count_b);
// for _ in 0..max_possible_swaps {
// let mut best_gain = std::i64::MIN;
// let mut best_pair: Option<(usize, usize)> = None;
// // 그룹 A와 그룹 B에서 각각 하나씩 선택
// for i in 0..n {
// if locked[i] || !partition[i] {
// continue;
// }
// for j in 0..n {
// if locked[j] || partition[j] {
// continue;
// }
// // 두 노드 사이의 연결 가중치는
// // (nodes[i] → nodes[j])와 (nodes[j] → nodes[i])의 가중치 합으로 계산
// let mut weight = 0i64;
// if let Some(edge) = graph.find_edge(nodes[i], nodes[j]) {
// weight += *graph.edge_weight(edge).unwrap() as i64;
// }
// if let Some(edge) = graph.find_edge(nodes[j], nodes[i]) {
// weight += *graph.edge_weight(edge).unwrap() as i64;
// }
// let gain = d[i] + d[j] - 2 * weight;
// if gain > best_gain {
// best_gain = gain;
// best_pair = Some((i, j));
// }
// }
// }
// if best_pair.is_none() {
// break;
// }
// let (a, b) = best_pair.unwrap();
// swap_pairs.push((a, b, best_gain));
// if gains.is_empty() {
// gains.push(best_gain);
// } else {
// gains.push(gains.last().unwrap() + best_gain);
// }
// locked[a] = true;
// locked[b] = true;
// // 아직 locked되지 않은 노드에 대해 D값 업데이트
// for k in 0..n {
// if locked[k] {
// continue;
// }
// let mut w_ka = 0i64;
// if let Some(edge) = graph.find_edge(nodes[k], nodes[a]) {
// w_ka += *graph.edge_weight(edge).unwrap() as i64;
// }
// if let Some(edge) = graph.find_edge(nodes[a], nodes[k]) {
// w_ka += *graph.edge_weight(edge).unwrap() as i64;
// }
// let mut w_kb = 0i64;
// if let Some(edge) = graph.find_edge(nodes[k], nodes[b]) {
// w_kb += *graph.edge_weight(edge).unwrap() as i64;
// }
// if let Some(edge) = graph.find_edge(nodes[b], nodes[k]) {
// w_kb += *graph.edge_weight(edge).unwrap() as i64;
// }
// if partition[k] {
// d[k] += 2 * (w_ka - w_kb);
// } else {
// d[k] += 2 * (w_kb - w_ka);
// }
// }
// }
// // 최적의 swap prefix (누적 gain 최대 구간)을 찾음
// let mut max_gain_total = std::i64::MIN;
// let mut k_max = None;
// for (k, &g) in gains.iter().enumerate() {
// if g > max_gain_total {
// max_gain_total = g;
// k_max = Some(k);
// }
// }
// // 누적 gain이 양수이면 swap 적용
// if max_gain_total > 0 {
// improvement = true;
// let num_swaps = k_max.unwrap() + 1;
// for i in 0..num_swaps {
// let (a, b, _) = swap_pairs[i];
// partition[a] = false;
// partition[b] = true;
// }
// }
// }
// partition
// }
// /// --- 2. 이진 탐색을 이용해 target_size에 가까운 bipartition을 구하는 함수 ---
// ///
// /// induced subgraph에서 초기 partition을 만들 때 그룹 A의 비율을 나타내는 파라미터 p를 이진 탐색하여,
// /// 최종 Kernighan–Lin 결과에서 그룹 A의 노드 수가 target_size에 최대한 가깝도록 한다.
// fn binary_partition(graph: &Graph<Node, usize, Directed>, target_size: usize) -> (Vec<bool>, f64) {
// let n = graph.node_count();
// let mut low = 0.0;
// let mut high = 1.0;
// let mut best_partition = vec![false; n];
// let mut best_diff = usize::MAX;
// let mut best_p = 0.5;
// // 20회 반복 (정밀도 조절 가능)
// for _ in 0..20 {
// let mid = (low + high) / 2.0;
// let mut init_partition = vec![false; n];
// let cutoff = ((mid * n as f64).floor() as usize).min(n);
// for i in 0..n {
// init_partition[i] = i < cutoff;
// }
// let partition = kernighan_lin_with_initial(graph, &init_partition);
// let size_a = partition.iter().filter(|&&b| b).count();
// let diff = if size_a > target_size {
// size_a - target_size
// } else {
// target_size - size_a
// };
// // 그룹 A의 크기가 target_size 이하인 경우 최적 갱신
// if size_a <= target_size && diff < best_diff {
// best_diff = diff;
// best_partition = partition.clone();
// best_p = mid;
// }
// if size_a > target_size {
// high = mid;
// } else {
// low = mid;
// }
// }
// (best_partition, best_p)
// }
// /// --- 3. 원 그래프에서 induced subgraph를 만드는 함수 ---
// ///
// /// 주어진 노드 집합에 대해 induced subgraph를 구성하며,
// /// 새 그래프의 노드 순서와 원래 노드 인덱스 사이의 매핑(mapping)도 함께 반환한다.
// fn induced_subgraph(
// graph: &Graph<Node, usize, Directed>,
// nodes: &HashSet<NodeIndex>,
// ) -> (Graph<Node, usize, Directed>, Vec<NodeIndex>) {
// let mut subgraph = Graph::<Node, usize, Directed>::new();
// let mut mapping = Vec::new(); // subgraph의 각 노드에 대응하는 원래 NodeIndex
// let mut node_map = HashMap::new(); // 원래 NodeIndex -> subgraph의 노드 인덱스
// // 노드 추가
// for &n in nodes.iter() {
// let new_idx = subgraph.add_node(Node);
// node_map.insert(n, new_idx);
// mapping.push(n);
// }
// // 간선 추가: 원래 그래프에서 n의 Outgoing 간선 중 target도 nodes 집합에 포함되면 추가
// for &n in nodes.iter() {
// let source_new = node_map[&n];
// for edge in graph.edges_directed(n, petgraph::Direction::Outgoing) {
// let target = edge.target();
// if nodes.contains(&target) {
// let target_new = node_map[&target];
// subgraph.add_edge(source_new, target_new, *edge.weight());
// }
// }
// }
// (subgraph, mapping)
// }
// /// --- 4. 재귀적(그리디한) 분할 ---
// ///
// /// 주어진 원 그래프의 노드 집합 `nodes`에 대해, 만약 노드 수가 `max_size` 이하이면 그대로 반환하고,
// /// 그렇지 않으면 induced subgraph를 구성한 후 binary_partition을 통해 bipartition한 다음,
// /// 각 부분에 대해 재귀적으로 분할하여 최종적으로 “각 그룹의 크기가 max_size 이하”인 그룹들을 구한다.
// fn greedy_partition(
// graph: &Graph<Node, usize, Directed>,
// nodes: &HashSet<NodeIndex>,
// max_size: usize,
// ) -> Vec<HashSet<NodeIndex>> {
// if nodes.len() <= max_size {
// return vec![nodes.clone()];
// }
// let (subgraph, mapping) = induced_subgraph(graph, nodes);
// let (partition, _p_used) = binary_partition(&subgraph, max_size);
// let mut group_a = HashSet::new();
// let mut group_b = HashSet::new();
// // induced subgraph의 노드 순서는 mapping을 통해 원래 노드 인덱스로 변환
// for (i, &in_a) in partition.iter().enumerate() {
// if in_a {
// group_a.insert(mapping[i]);
// } else {
// group_b.insert(mapping[i]);
// }
// }
// let mut result = Vec::new();
// if group_a.len() > max_size {
// result.extend(greedy_partition(graph, &group_a, max_size));
// } else {
// result.push(group_a);
// }
// if group_b.len() > max_size {
// result.extend(greedy_partition(graph, &group_b, max_size));
// } else {
// result.push(group_b);
// }
// result
// }
// #[test]
// fn test_main() {
// // 예제: 간단한 Directed 그래프 생성 (노드 타입: Node, 가중치: usize)
// let mut graph = Graph::<Node, usize, Directed>::new();
// // 노드 추가
// let a = graph.add_node(Node);
// let b = graph.add_node(Node);
// let c = graph.add_node(Node);
// let d = graph.add_node(Node);
// let e = graph.add_node(Node);
// let f = graph.add_node(Node);
// let g = graph.add_node(Node);
// let h = graph.add_node(Node);
// // 엣지 추가 (방향 그래프)
// graph.add_edge(a, b, 5);
// graph.add_edge(a, c, 3);
// graph.add_edge(b, d, 4);
// graph.add_edge(c, d, 2);
// graph.add_edge(c, e, 6);
// graph.add_edge(d, f, 3);
// graph.add_edge(e, f, 1);
// graph.add_edge(e, g, 2);
// graph.add_edge(f, h, 4);
// graph.add_edge(g, h, 3);
// // 전체 노드 집합
// let all_nodes: HashSet<NodeIndex> = graph.node_indices().collect();
// // 예: 각 그룹의 최대 노드 수를 3개로 제한
// let max_size = 3;
// let groups = greedy_partition(&graph, &all_nodes, max_size);
// println!("총 {}개의 그룹:", groups.len());
// for (i, group) in groups.iter().enumerate() {
// println!("그룹 {} ({}개 노드): {:?}", i + 1, group.len(), group);
// }
// }
// #[cfg(test)]
// mod tests {
// use std::collections::HashSet;
// use petgraph::graph::Graph;
// use petgraph::Directed;
// use super::*;
// // 1. 빈 그래프: 노드가 하나도 없으면 그룹은 [∅]로 반환되어야 함.
// #[test]
// fn test_empty_graph() {
// let graph: Graph<Node, usize, Directed> = Graph::new();
// let nodes: HashSet<NodeIndex> = graph.node_indices().collect();
// let groups = greedy_partition(&graph, &nodes, 3);
// // 빈 그래프의 경우 하나의 그룹(빈 집합)이 반환됨
// assert_eq!(groups.len(), 1);
// assert!(groups[0].is_empty());
// }
// // 2. 단일 노드: 하나의 노드만 있으면 해당 노드가 포함된 그룹이 반환되어야 함.
// #[test]
// fn test_single_node() {
// let mut graph = Graph::<Node, usize, Directed>::new();
// let a = graph.add_node(Node);
// let nodes: HashSet<NodeIndex> = graph.node_indices().collect();
// let groups = greedy_partition(&graph, &nodes, 1);
// assert_eq!(groups.len(), 1);
// assert_eq!(groups[0].len(), 1);
// assert!(groups[0].contains(&a));
// }
// // 3. 두 노드, 간선 없음: 두 노드가 각각 분리되어 반환되어야 함.
// #[test]
// fn test_two_nodes_no_edge() {
// let mut graph = Graph::<Node, usize, Directed>::new();
// let a = graph.add_node(Node);
// let b = graph.add_node(Node);
// let nodes: HashSet<NodeIndex> = graph.node_indices().collect();
// // max_size = 1이면 두 노드는 서로 다른 그룹에 분리되어야 함.
// let groups = greedy_partition(&graph, &nodes, 1);
// assert_eq!(groups.len(), 2);
// for group in groups {
// assert_eq!(group.len(), 1);
// }
// }
// // 4. 두 노드, 간선 있음: 양방향 간선을 추가하여 두 노드가 분리되어야 함.
// #[test]
// fn test_two_nodes_one_edge() {
// let mut graph = Graph::<Node, usize, Directed>::new();
// let a = graph.add_node(Node);
// let b = graph.add_node(Node);
// graph.add_edge(a, b, 5);
// graph.add_edge(b, a, 5);
// let nodes: HashSet<NodeIndex> = graph.node_indices().collect();
// let groups = greedy_partition(&graph, &nodes, 1);
// assert_eq!(groups.len(), 2);
// for group in groups {
// assert_eq!(group.len(), 1);
// }
// }
// // 5. 체인 형태의 그래프: 4개의 노드가 일렬로 연결되어 있을 때, max_size = 2면 두 그룹으로 분할됨.
// #[test]
// fn test_chain_graph() {
// let mut graph = Graph::<Node, usize, Directed>::new();
// let a = graph.add_node(Node);
// let b = graph.add_node(Node);
// let c = graph.add_node(Node);
// let d = graph.add_node(Node);
// graph.add_edge(a, b, 1);
// graph.add_edge(b, a, 1);
// graph.add_edge(b, c, 1);
// graph.add_edge(c, b, 1);
// graph.add_edge(c, d, 1);
// graph.add_edge(d, c, 1);
// let nodes: HashSet<NodeIndex> = graph.node_indices().collect();
// let groups = greedy_partition(&graph, &nodes, 2);
// for group in groups.iter() {
// assert!(group.len() <= 2);
// }
// let total: usize = groups.iter().map(|g| g.len()).sum();
// assert_eq!(total, 4);
// }
// // 6. 양분된(바이파티트) 그래프: 6개의 노드 (3+3)로 구성되어, max_size = 3이면 2개의 그룹이 되어야 함.
// #[test]
// fn test_bipartite_graph() {
// let mut graph = Graph::<Node, usize, Directed>::new();
// let a = graph.add_node(Node);
// let b = graph.add_node(Node);
// let c = graph.add_node(Node);
// let d = graph.add_node(Node);
// let e = graph.add_node(Node);
// let f = graph.add_node(Node);
// // 강한 intra-group 간선 (양방향, 가중치 10)
// graph.add_edge(a, b, 10);
// graph.add_edge(b, a, 10);
// graph.add_edge(a, c, 10);
// graph.add_edge(c, a, 10);
// graph.add_edge(b, c, 10);
// graph.add_edge(c, b, 10);
// graph.add_edge(d, e, 10);
// graph.add_edge(e, d, 10);
// graph.add_edge(d, f, 10);
// graph.add_edge(f, d, 10);
// graph.add_edge(e, f, 10);
// graph.add_edge(f, e, 10);
// // 약한 inter-group 간선 (가중치 1)
// graph.add_edge(a, d, 1);
// graph.add_edge(d, a, 1);
// graph.add_edge(b, e, 1);
// graph.add_edge(e, b, 1);
// graph.add_edge(c, f, 1);
// graph.add_edge(f, c, 1);
// let nodes: HashSet<NodeIndex> = graph.node_indices().collect();
// let groups = greedy_partition(&graph, &nodes, 3);
// for group in groups.iter() {
// assert!(group.len() <= 3);
// }
// let total: usize = groups.iter().map(|g| g.len()).sum();
// assert_eq!(total, 6);
// }
// // 7. 사이클 그래프: 4개의 노드가 사이클을 이루며, max_size = 2이면 두 그룹으로 분할됨.
// #[test]
// fn test_cycle_graph() {
// let mut graph = Graph::<Node, usize, Directed>::new();
// let a = graph.add_node(Node);
// let b = graph.add_node(Node);
// let c = graph.add_node(Node);
// let d = graph.add_node(Node);
// graph.add_edge(a, b, 2);
// graph.add_edge(b, a, 2);
// graph.add_edge(b, c, 2);
// graph.add_edge(c, b, 2);
// graph.add_edge(c, d, 2);
// graph.add_edge(d, c, 2);
// graph.add_edge(d, a, 2);
// graph.add_edge(a, d, 2);
// let nodes: HashSet<NodeIndex> = graph.node_indices().collect();
// let groups = greedy_partition(&graph, &nodes, 2);
// for group in groups.iter() {
// assert!(group.len() <= 2);
// }
// let total: usize = groups.iter().map(|g| g.len()).sum();
// assert_eq!(total, 4);
// }
// // 8. 완전 그래프: 4개의 노드가 모두 연결되어 있고, max_size = 2이면 각 그룹의 크기는 2 이하.
// #[test]
// fn test_complete_graph() {
// let mut graph = Graph::<Node, usize, Directed>::new();
// let a = graph.add_node(Node);
// let b = graph.add_node(Node);
// let c = graph.add_node(Node);
// let d = graph.add_node(Node);
// let nodes: Vec<NodeIndex> = graph.node_indices().collect();
// for i in 0..nodes.len() {
// for j in 0..nodes.len() {
// if i != j {
// graph.add_edge(nodes[i], nodes[j], 1);
// }
// }
// }
// let all_nodes: HashSet<NodeIndex> = graph.node_indices().collect();
// let groups = greedy_partition(&graph, &all_nodes, 2);
// for group in groups.iter() {
// assert!(group.len() <= 2);
// }
// let total: usize = groups.iter().map(|g| g.len()).sum();
// assert_eq!(total, 4);
// }
// // 9. binary_partition 함수 테스트: 4개 노드 그래프에서 target_size = 2이면 그룹 A의 크기는 2 이하.
// #[test]
// fn test_binary_partition_function() {
// let mut graph = Graph::<Node, usize, Directed>::new();
// let a = graph.add_node(Node);
// let b = graph.add_node(Node);
// let c = graph.add_node(Node);
// let d = graph.add_node(Node);
// graph.add_edge(a, b, 3);
// graph.add_edge(b, a, 3);
// graph.add_edge(c, d, 2);
// graph.add_edge(d, c, 2);
// let (partition, p_used) = binary_partition(&graph, 2);
// let size_a = partition.iter().filter(|&&b| b).count();
// assert!(size_a <= 2);
// assert!(p_used >= 0.0 && p_used <= 1.0);
// }
// // 10. induced_subgraph 함수 테스트: 원래 그래프에서 일부 노드를 선택하여 induced subgraph가 올바르게 구성되어야 함.
// #[test]
// fn test_induced_subgraph_function() {
// let mut graph = Graph::<Node, usize, Directed>::new();
// let a = graph.add_node(Node);
// let b = graph.add_node(Node);
// let c = graph.add_node(Node);
// let d = graph.add_node(Node);
// let e = graph.add_node(Node);
// graph.add_edge(a, b, 4);
// graph.add_edge(b, a, 4);
// graph.add_edge(b, c, 5);
// graph.add_edge(c, b, 5);
// graph.add_edge(c, d, 6);
// graph.add_edge(d, c, 6);
// graph.add_edge(d, e, 7);
// graph.add_edge(e, d, 7);
// let subset: HashSet<NodeIndex> = [a, c, e].iter().cloned().collect();
// let (subgraph, mapping) = induced_subgraph(&graph, &subset);
// assert_eq!(subgraph.node_count(), 3);
// assert_eq!(mapping.len(), 3);
// for edge in subgraph.edge_references() {
// let source = mapping[edge.source().index()];
// let target = mapping[edge.target().index()];
// assert!(graph.find_edge(source, target).is_some());
// }
// }
// }Metadata
Metadata
Assignees
Labels
No labels