@@ -1407,7 +1407,7 @@ deleteAt !i t =
1407
1407
where
1408
1408
sizeL = size l
1409
1409
1410
- -- | Take a given number of elements in order, beginning
1410
+ -- | \(O(\log n)\). Take a given number of elements in order, beginning
1411
1411
-- with the smallest ones.
1412
1412
--
1413
1413
-- @
@@ -1428,7 +1428,7 @@ take i0 m0 = go i0 m0
1428
1428
EQ -> l
1429
1429
where sizeL = size l
1430
1430
1431
- -- | Drop a given number of elements in order, beginning
1431
+ -- | \(O(\log n)\). Drop a given number of elements in order, beginning
1432
1432
-- with the smallest ones.
1433
1433
--
1434
1434
-- @
@@ -1809,7 +1809,7 @@ splitRoot orig =
1809
1809
{-# INLINE splitRoot #-}
1810
1810
1811
1811
1812
- -- | Calculate the power set of a set: the set of all its subsets.
1812
+ -- | \(O(2^n \log n)\). Calculate the power set of a set: the set of all its subsets.
1813
1813
--
1814
1814
-- @
1815
1815
-- t ``member`` powerSet s == t ``isSubsetOf`` s
@@ -1823,11 +1823,22 @@ splitRoot orig =
1823
1823
-- @
1824
1824
--
1825
1825
-- @since 0.5.11
1826
+
1827
+ -- Proof of complexity: step executes n times. At the ith step,
1828
+ -- "insertMin x `mapMonotonic` pxs" takes O(2^i log i) time since pxs has size
1829
+ -- 2^i - 1 and we insertMin into its elements which are sets of size <= i.
1830
+ -- "insertMin (singleton x)" and "`glue` pxs" are cheaper operations that both
1831
+ -- take O(i) time. Over n steps, we have a total cost of
1832
+ --
1833
+ -- O(\sum_{i=1}^{n-1} 2^i log i)
1834
+ -- = O(log n * \sum_{i=1}^{n-1} 2^i)
1835
+ -- = O(2^n log n)
1836
+
1826
1837
powerSet :: Set a -> Set (Set a )
1827
1838
powerSet xs0 = insertMin empty (foldr' step Tip xs0) where
1828
1839
step x pxs = insertMin (singleton x) (insertMin x `mapMonotonic` pxs) `glue` pxs
1829
1840
1830
- -- | \(O(mn)\) (conjectured ). Calculate the Cartesian product of two sets.
1841
+ -- | \(O(nm)\ ). Calculate the Cartesian product of two sets.
1831
1842
--
1832
1843
-- @
1833
1844
-- cartesianProduct xs ys = fromList $ liftA2 (,) (toList xs) (toList ys)
@@ -1842,11 +1853,7 @@ powerSet xs0 = insertMin empty (foldr' step Tip xs0) where
1842
1853
--
1843
1854
-- @since 0.5.11
1844
1855
cartesianProduct :: Set a -> Set b -> Set (a , b )
1845
- -- I don't know for sure if this implementation (slightly modified from one
1846
- -- that Edward Kmett hacked together) is optimal. TODO: try to prove or
1847
- -- refute it.
1848
- --
1849
- -- We could definitely get big-O optimal (O(m * n)) in a rather simple way:
1856
+ -- The obvious big-O optimal (O(nm)) implementation would be
1850
1857
--
1851
1858
-- cartesianProduct _as Tip = Tip
1852
1859
-- cartesianProduct as bs = fromDistinctAscList
@@ -1855,8 +1862,31 @@ cartesianProduct :: Set a -> Set b -> Set (a, b)
1855
1862
-- Unfortunately, this is much slower in practice, at least when the sets are
1856
1863
-- constructed from ascending lists. I tried doing the same thing using a
1857
1864
-- known-length (perfect balancing) variant of fromDistinctAscList, but it
1858
- -- still didn't come close to the performance of Kmett's version in my very
1859
- -- informal tests.
1865
+ -- still didn't come close to the performance of the implementation we use in my
1866
+ -- very informal tests.
1867
+ --
1868
+ -- The implementation we use (slightly modified from one that Edward Kmett
1869
+ -- hacked together) is also optimal but performs better in practice. We map
1870
+ -- each element a in as to a set made up of (a,b) for every element b in bs,
1871
+ -- taking O(nm) overall. Then we merge these sets up the tree of as, which takes
1872
+ -- O(n log m). A brief sketch of proof for the latter:
1873
+ --
1874
+ -- Consider all nodes in the tree at the same distance from the root to be at
1875
+ -- the same "level". The nodes farthest from the root are at level 0, with
1876
+ -- levels increasing by 1 towards the root. Being a balanced tree, there are
1877
+ -- O(n/2^i) nodes at level i. At every node at level i, we merge the merged left
1878
+ -- set, current set, and merged right set into a set of size O(2^i*m) in
1879
+ -- O(log (2^i*m)) = O(i + log m) time. Over all levels, we do a total work of
1880
+ --
1881
+ -- O(\sum_{i=0}^{root_level} n * (i + log m) / 2^i)
1882
+ -- = O( \sum_{i=0}^{root_level} n * i / 2^i
1883
+ -- + \sum_{i=0}^{root_level} n * log m / 2^i)
1884
+ -- = O( n * \sum_{i=0}^{root_level} i/2^i
1885
+ -- + n * log m * \sum_{i=0}^{root_level} 1/2^i)
1886
+ -- = O( n * \sum_{i=0}^{inf} i/2^i
1887
+ -- + n * log m * \sum_{i=0}^{inf} 1/2^i)
1888
+ --
1889
+ -- The sum terms converge, and we get O(n log m).
1860
1890
1861
1891
-- When the second argument has at most one element, we can be a little
1862
1892
-- clever.
@@ -1879,7 +1909,7 @@ instance Monoid (MergeSet a) where
1879
1909
1880
1910
mappend = (<>)
1881
1911
1882
- -- | Calculate the disjoint union of two sets.
1912
+ -- | \(O(n+m)\). Calculate the disjoint union of two sets.
1883
1913
--
1884
1914
-- @ disjointUnion xs ys = map Left xs ``union`` map Right ys @
1885
1915
--
@@ -1897,14 +1927,14 @@ disjointUnion as bs = merge (mapMonotonic Left as) (mapMonotonic Right bs)
1897
1927
{- -------------------------------------------------------------------
1898
1928
Debugging
1899
1929
--------------------------------------------------------------------}
1900
- -- | \(O(n)\). Show the tree that implements the set. The tree is shown
1930
+ -- | \(O(n \log n )\). Show the tree that implements the set. The tree is shown
1901
1931
-- in a compressed, hanging format.
1902
1932
showTree :: Show a => Set a -> String
1903
1933
showTree s
1904
1934
= showTreeWith True False s
1905
1935
1906
1936
1907
- {- | \(O(n)\). The expression (@showTreeWith hang wide map@) shows
1937
+ {- | \(O(n \log n )\). The expression (@showTreeWith hang wide map@) shows
1908
1938
the tree that implements the set. If @hang@ is
1909
1939
@True@, a /hanging/ tree is shown otherwise a rotated tree is shown. If
1910
1940
@wide@ is 'True', an extra wide version is shown.
0 commit comments