def solve_binary_pick_k(self, show_spent_time: bool = True) -> list: """ 借助Binary Search的思想,每次從post_list中N篇文章中找出相對排序最前面1個,重複k次 時間複雜度 O(k log N) 空間複雜度 O(N) Returns: [list]: Top-K articles """ if show_spent_time: start = time.time() top_k_posts_list = [] n_picks = 0 post_set = IndexedSet(self.post_list) # 一個重複k次的迴圈,時間複雜度 O(k) while n_picks < self.top_k: # binary pick,採Divide and Conquer,時間複雜度 O(log N) curr_best_post = binary_pick( self.ranker, post_set, left=0, right=len(post_set) - 1) top_k_posts_list.append(curr_best_post) # 加入元素到list 時間複雜度 O(1) # 從IndexableSet移除元素,時間複雜度O(1) (HashTable) post_set.remove(curr_best_post) n_picks += 1 if show_spent_time: spent = (time.time() - start) * 1000 print(self) print(f'spent time {spent:.2f} ms') return top_k_posts_list
class GNodeSocket(QGraphicsWidget): edge_started = pyqtSignal(GEdge) edge_released = pyqtSignal(GEdge) connection_changed = pyqtSignal(object, object) # Socket, Edge or None position_changed = pyqtSignal(QPointF) INPUT = SocketType.INPUT OUTPUT = SocketType.OUTPUT def __init__(self, parent_node: 'GShaderNode', socket: NodeSocket): super().__init__(parent=parent_node) self._socket = socket self._socket.set_container(self) self._parent_g_node = parent_node self._connected_g_edges = IndexedSet() # Define socket properties self._circle_connected_brush = QColor( 255, 130, 0, 255) if self._socket.type() == NodeSocket.INPUT else QColor( 130, 255, 0, 255) self._circle_disconnected_brush = QColor( 102, 50, 0, 255) if self._socket.type() == NodeSocket.INPUT else QColor( 50, 102, 0, 255) self._circle_hover_brush = QColor( 170, 130, 0, 255) if self._socket.type() == NodeSocket.INPUT else QColor( 130, 170, 0, 255) self._border_connected_brush = QPen(QColor(255, 255, 255, 255)) self._border_disconnected_brush = QPen(Qt.black) self._circle_brush = self._circle_disconnected_brush self._border_brush = self._border_disconnected_brush self._bbox = QRectF(0, 0, 10, 10) self._moving_edge = False self._current_edge = None self._layout = QGraphicsLinearLayout(Qt.Horizontal) self._init_socket() def _init_socket(self): self.setLayout(self._layout) self.setFlag(QGraphicsItem.ItemIsMovable, False) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) self.setAcceptHoverEvents(True) def type(self) -> SocketType: return self._socket.type() def value(self): return self._socket.value() def set_value(self, value: typing.Any): self._socket.set_value(value) def save_value(self): """Saves the value of this socket internally. This value can be reassigned by calling 'restore_value()'.""" self._socket.save_value() def restore_value(self): """Restores the value of this socket to the last saved value.""" self._socket.restore_value() def parent_node(self) -> 'GShaderNode': return self._parent_g_node def get_size(self): """Returns a tuple with the width,height of this socket.""" return self._bbox.right(), self._bbox.bottom() def get_backend_socket(self) -> NodeSocket: return self._socket def get_connected_nodes(self) -> typing.List['GShaderNode']: return [n.get_container() for n in self._socket.get_connected_nodes()] def get_connected_sockets(self) -> typing.List['GNodeSocket']: return [ s.get_container() for s in self._socket.get_connected_sockets() ] def set_index(self, index: int): self._socket.set_index(index) def get_index(self) -> int: return self._socket.get_index() def is_connected(self) -> bool: return self._socket.is_connected() def boundingRect(self): return self._bbox def paint(self, painter: QPainter, option, widget=None): painter.setPen(self._border_brush) painter.setBrush(self._circle_brush) painter.drawEllipse(self._bbox) def get_scene_pos(self) -> QPointF: """Gets the center position of this socket in scene coordinates.""" pos = self.scenePos() pos.setX(pos.x() + self._bbox.right() / 2) pos.setY(pos.y() + self._bbox.bottom() / 2) return pos def connect_to(self, socket: 'GNodeSocket') -> GEdge: """ Connects this GNodeSocket to another GNodeSocket. :param other_socket: Other GNodeSocket to connect this socket to. :return: the GEdge that was created between the sockets, or the old GEdge if there already exists a connection. """ edge = self._socket.connect_to(socket.get_backend_socket()) # Only emit change event for input sockets, as nothing really changed for the output socket (at least not for the node as a whole) if self.type() == SocketType.INPUT: self.connection_changed.emit(self, edge) elif socket.type() == SocketType.INPUT: socket.connection_changed.emit(socket, edge) return GEdge.from_edge(edge) def get_connected_edges(self) -> IndexedSet: return self._connected_g_edges def add_connecting_edge(self, edge: GEdge): self._connected_g_edges.add(edge) def remove_connected_edge(self, gedge: GEdge): assert gedge in self._connected_g_edges self._connected_g_edges.remove(gedge) def label(self) -> str: return self._socket.label() def __eq__(self, other): if isinstance(other, GNodeSocket): return self._socket.__eq__(other.get_backend_socket()) return False def __hash__(self): return self._socket.__hash__() def __str__(self): return self._socket.__str__() # -------- Event Handling --------- # ---------------------------------- def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent): # self._circle_brush = self._circle_hover_brush self._border_brush = self._border_connected_brush self.update() event.accept() def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent): # self._circle_brush = self._circle_connected_brush if self.connected else self._circle_disconnected_brush if not self.is_connected(): self._border_brush = self._border_disconnected_brush self.update() event.accept() def mousePressEvent(self, event: QGraphicsSceneMouseEvent): # Needs to be reimplemented to be able to catch mouseMoveEvent, but it does not need to do anything event.accept() def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent): if event.buttons() == Qt.LeftButton: if not self._moving_edge: if self.type() == self.INPUT: edge = GEdge(destination=self) else: edge = GEdge(source=self) self._current_edge = edge self._moving_edge = True self.edge_started.emit(edge) else: self._current_edge.set_tip_pos(event.scenePos()) event.accept() else: event.ignore() def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent): if self._current_edge: # Prevent release without dragging self.edge_released.emit(self._current_edge) self._current_edge = None self._moving_edge = False def itemChange(self, change, value): if change == self.ItemScenePositionHasChanged: for edge in self._connected_g_edges: edge.update_edge() return super().itemChange(change, value)
class NodeSocket(GraphElement): INPUT: SocketType = SocketType.INPUT OUTPUT: SocketType = SocketType.OUTPUT def __init__(self, parent_node: 'Node', socket_type: SocketType, dtype: DataType = None, label: str = "", container=None): super().__init__(container) assert isinstance(socket_type, SocketType), "socket_type must be of enum type SocketType!" self._parent_g_node = parent_node self._type = socket_type self._dtype = dtype self._label = label self._id = uuid.uuid4() self._index = -1 self._value = None self._saved_value = None self._connected = False self._connected_sockets = IndexedSet() self._connected_edges = IndexedSet() def connect_to(self, other_socket: 'NodeSocket') -> Edge: """ Connects this NodeSocket to another NodeSocket. :param other_socket: Other NodeSocket to connect this socket to. :return: the Edge that was created between the sockets, or the old Edge if there already exists a connection. """ assert self.id() != other_socket.id(), "Can not connect socket to itself!" assert self.type() != other_socket.type(), "Can not connect sockets of the same type!" # Check if this socket is already connected to 'socket' if other_socket in self._connected_sockets: edge = self.find_connecting_edge(other_socket) return edge # Create a directed edge if self.type() == NodeSocket.OUTPUT: edge = Edge(source=self, destination=other_socket) else: edge = Edge(source=other_socket, destination=self) self._connected_edges.add(edge) other_socket._connected_edges.add(edge) self._connected_sockets.add(other_socket) other_socket._connected_sockets.add(self) self._connected = True other_socket._connected = True return edge def disconnect_all(self): """Disconnects this NodeSocket from all other connected sockets.""" for other in self._connected_sockets: self.disconnect_from(other) def disconnect_from(self, other_socket: 'NodeSocket'): """ Disconnects this NodeSocket from another NodeSocket. :param other_socket: Another NodeSocket to disconnect from. """ assert other_socket in self._connected_sockets, "Can not disconnect from a socket that is not connected!" edge = self.find_connecting_edge(other_socket) if edge is not None: self._connected_sockets.remove(other_socket) other_socket._connected_sockets.remove(self) self._connected_edges.remove(edge) other_socket._connected_edges.remove(edge) del edge self._connected = False other_socket._connected = False else: raise RuntimeError("Sockets are indicated as connected but could not find their connecting Edge. The Node Graph is corrupt!") def get_parent_node(self) -> 'Node': return self._parent_g_node def get_connected_nodes(self) -> typing.List['Node']: """ Returns each node that is connected to this NodeSocket. :return: A list of Nodes connected to this NodeSocket. """ nodes = [] for socket in self._connected_sockets: nodes.append(socket.get_parent_node()) return nodes def get_connected_sockets(self) -> list: """ Returns each NodeSocket that this NodeSocket is connected to. :return: A list of NodeSockets connected to this NodeSocket. """ return list(self._connected_sockets) def set_index(self, index): """Set the index of this socket. This value is used to figure out what input/output this socket corresponds to.""" self._index = index def get_index(self) -> int: """Gets the index of this socket.""" return self._index def set_value(self, value: typing.Any): self._value = value def save_value(self): """Saves the value of this socket internally. This value can be reassigned by calling 'restore_value()'.""" self._saved_value = self._value.detach().clone() def restore_value(self): """Restores the value of this socket to the last saved value.""" self._value = self._saved_value.detach().clone() def value(self) -> typing.Any: return self._value def label(self) -> str: return self._label def is_connected(self) -> bool: """Returns a boolean indicating whether this socket is connected to any other sockets with an Edge.""" return self._connected def type(self) -> SocketType: """Returns the type of this NodeSocket indicating whether it accepts input or returns output.""" return self._type def dtype(self) -> DataType: return self._dtype def find_connecting_edge(self, other_socket: 'NodeSocket') -> typing.Union[None, Edge]: for edge in self._connected_edges: if edge.connects(self, other_socket): return edge return None