| 464 | def RandomDirectedGNM(self, n, m, loops = False): |
| 465 | r""" |
| 466 | Returns a random labelled digraph on `n` nodes and `m` arcs. |
| 467 | |
| 468 | INPUT: |
| 469 | |
| 470 | - ``n`` (integer) -- number of vertices. |
| 471 | |
| 472 | - ``m`` (integer) -- number of edges. |
| 473 | |
| 474 | - ``loops`` (boolean) -- whether to allow loops (set to ``False`` by |
| 475 | default). |
| 476 | |
| 477 | PLOTTING: When plotting, this graph will use the default spring-layout |
| 478 | algorithm, unless a position dictionary is specified. |
| 479 | |
| 480 | EXAMPLE:: |
| 481 | |
| 482 | sage: D = digraphs.RandomDirectedGNM(10, 5) |
| 483 | sage: D.num_verts() |
| 484 | 10 |
| 485 | sage: D.edges(labels=False) |
| 486 | [(0, 3), (1, 5), (5, 1), (7, 0), (8, 5)] |
| 487 | |
| 488 | With loops:: |
| 489 | |
| 490 | sage: D = digraphs.RandomDirectedGNM(10, 100, loops = True) |
| 491 | sage: D.num_verts() |
| 492 | 10 |
| 493 | sage: D.loops() |
| 494 | [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None), (4, 4, None), (5, 5, None), (6, 6, None), (7, 7, None), (8, 8, None), (9, 9, None)] |
| 495 | |
| 496 | TESTS:: |
| 497 | |
| 498 | sage: digraphs.RandomDirectedGNM(10,-3) |
| 499 | Traceback (most recent call last): |
| 500 | ... |
| 501 | ValueError: The number of edges must satisfy 0<= m <= n(n-1) when no loops are allowed, and 0<= m <= n^2 otherwise. |
| 502 | |
| 503 | sage: digraphs.RandomDirectedGNM(10,100) |
| 504 | Traceback (most recent call last): |
| 505 | ... |
| 506 | ValueError: The number of edges must satisfy 0<= m <= n(n-1) when no loops are allowed, and 0<= m <= n^2 otherwise. |
| 507 | """ |
| 508 | n, m = int(n), int(m) |
| 509 | |
| 510 | # The random graph is built by drawing randomly and uniformly two |
| 511 | # integers u,v, and adding the corresponding edge if it does not exist, |
| 512 | # as many times as necessary. |
| 513 | |
| 514 | # When the graph is dense, we actually compute its complement. This will |
| 515 | # prevent us from drawing the same pair u,v too many times. |
| 516 | |
| 517 | from sage.misc.prandom import _pyrand |
| 518 | rand = _pyrand() |
| 519 | D = DiGraph(n, loops = loops) |
| 520 | |
| 521 | # Ensuring the parameters n,m make sense. |
| 522 | # |
| 523 | # If the graph is dense, we actually want to build its complement. We |
| 524 | # update m accordingly. |
| 525 | |
| 526 | good_input = True |
| 527 | is_dense = False |
| 528 | |
| 529 | if m < 0: |
| 530 | good_input = False |
| 531 | |
| 532 | if loops: |
| 533 | if m > n*n: |
| 534 | good_input = False |
| 535 | elif m > n*n/2: |
| 536 | is_dense = True |
| 537 | m = n*n - m |
| 538 | |
| 539 | else: |
| 540 | if m > n*(n-1): |
| 541 | good_input = False |
| 542 | elif m > n*(n-1)/2: |
| 543 | is_dense = True |
| 544 | m = n*(n-1) - m |
| 545 | |
| 546 | if not good_input: |
| 547 | raise ValueError("The number of edges must satisfy 0<= m <= n(n-1) when no loops are allowed, and 0<= m <= n^2 otherwise.") |
| 548 | |
| 549 | # When the given number of edges defines a density larger than 1/2, it |
| 550 | # should be faster to compute the complement of the graph (less edges to |
| 551 | # generate), then to return its complement. This being said, the |
| 552 | # .complement() method for sparse graphs is very slow at the moment. |
| 553 | |
| 554 | # Similarly, it is faster to test whether a pair belongs to a dictionary |
| 555 | # than to test the adjacency of two vertices in a graph. For these |
| 556 | # reasons, the following code mainly works on dictionaries. |
| 557 | |
| 558 | adj = dict( (i, dict()) for i in range(n) ) |
| 559 | |
| 560 | # We fill the dictionary structure, but add the corresponding edge in |
| 561 | # the graph only if is_dense is False. If it is true, we will add the |
| 562 | # edges in a second phase. |
| 563 | |
| 564 | |
| 565 | while m > 0: |
| 566 | |
| 567 | # It is better to obtain random numbers this way than by calling the |
| 568 | # randint or randrange method. This, because they are very expensive |
| 569 | # when trying to compute MANY random integers, and because the |
| 570 | # following lines is precisely what they do anyway, after checking |
| 571 | # their parameters are correct. |
| 572 | |
| 573 | u=int(rand.random()*n) |
| 574 | v=int(rand.random()*n) |
| 575 | |
| 576 | if (u != v or loops) and (not v in adj[u]): |
| 577 | adj[u][v] = 1 |
| 578 | m -= 1 |
| 579 | if not is_dense: |
| 580 | D.add_edge(u,v) |
| 581 | |
| 582 | # If is_dense is True, it means the graph has not been built. We fill D |
| 583 | # with the complement of the edges stored in the adj dictionary |
| 584 | |
| 585 | if is_dense: |
| 586 | for u in range(n): |
| 587 | for v in range(n): |
| 588 | if ((u != v) or loops) and (not (v in adj[u])): |
| 589 | D.add_edge(u,v) |
| 590 | |
| 591 | return D |
| 592 | |